Resi cue countdowns on ProPresenter and in Companion
The Resi -> ProPresenter bridge, its installer and site kit, and the Companion module resi-studio-cues, as running at North Attleboro. Bridge (resi_to_propresenter.py) - Follows a Resi decoder's playhead and keeps ProPresenter's NextCue timer counting down to the next cue; optional cue name as a stage message, sent once per cue (ProPresenter flashes it on every send) and off by default. - Playhead estimate no longer learns a cadence from the partial first gap after a start, so a 30s Resi doesn't read as paused half the time. - Backs off on 429, holds 2 min before retrying a failed startup login, and logs Resi's actual reason (wrong password, USER_LOCKED, rate limit). - --setup asks for the login, decoder, ProPresenter and timer. install_bridge.sh installs it as a login service (outside ~/Documents, which launchd may not read); make_kit.sh builds the kit for another site. Companion module resi-studio-cues 0.1.4 (source/, package committed) - Same playhead fix; login and request hold-off (15s doubling to 5 min); a 401/403 on a fresh session is a permission, not a reason to sign in again; init never fails on a Resi problem, so Companion can't restart it into a login loop. Tests: test_playhead.py, test_stage_message.py, test_rate_limit.py and source/backoff_test.js run offline; test_end_to_end.py drives a real ProPresenter and refuses to run without an explicit flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vendored
+184
@@ -0,0 +1,184 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UpdateActions = UpdateActions;
|
||||
function UpdateActions(self) {
|
||||
self.setActionDefinitions({
|
||||
refresh_profile: {
|
||||
name: 'Refresh profile (users/me)',
|
||||
options: [],
|
||||
callback: async () => {
|
||||
await self.refreshProfileNow();
|
||||
},
|
||||
},
|
||||
refresh_lists: {
|
||||
name: 'Refresh venues/players list',
|
||||
options: [],
|
||||
callback: async () => {
|
||||
await self.refreshListsNow();
|
||||
},
|
||||
},
|
||||
add_cue_at_position: {
|
||||
name: 'Add cue at player position (or custom time)',
|
||||
options: [
|
||||
{
|
||||
id: 'name',
|
||||
type: 'textinput',
|
||||
label: 'Cue name',
|
||||
default: 'Web Cue',
|
||||
useVariables: true,
|
||||
},
|
||||
{
|
||||
id: 'privateCue',
|
||||
type: 'checkbox',
|
||||
label: 'Private cue',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: 'positionMode',
|
||||
type: 'dropdown',
|
||||
label: 'Position source',
|
||||
default: 'player',
|
||||
choices: [
|
||||
{ id: 'player', label: 'Player position (current)' },
|
||||
{ id: 'custom', label: 'Custom time' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'customPosition',
|
||||
type: 'textinput',
|
||||
label: 'Custom position (HH:MM:SS or HH:MM:SS.mmm)',
|
||||
default: '00:00:00.000',
|
||||
useVariables: true,
|
||||
},
|
||||
],
|
||||
callback: async (evt) => {
|
||||
const name = (await self.parseVariablesInString(String(evt.options.name ?? ''))).trim();
|
||||
const privateCue = Boolean(evt.options.privateCue);
|
||||
const positionMode = String(evt.options.positionMode ?? 'player');
|
||||
const customPosition = (await self.parseVariablesInString(String(evt.options.customPosition ?? ''))).trim();
|
||||
await self.addCueFromPosition({
|
||||
name,
|
||||
privateCue,
|
||||
positionMode,
|
||||
customPosition,
|
||||
});
|
||||
},
|
||||
},
|
||||
delete_cue_by_name: {
|
||||
name: 'Delete cue by name',
|
||||
options: [
|
||||
{
|
||||
id: 'cueName',
|
||||
type: 'textinput',
|
||||
label: 'Cue name',
|
||||
default: '',
|
||||
useVariables: true,
|
||||
},
|
||||
{
|
||||
id: 'matchMode',
|
||||
type: 'dropdown',
|
||||
label: 'Match mode',
|
||||
default: 'exact',
|
||||
choices: [
|
||||
{ id: 'exact', label: 'Exact (case-insensitive)' },
|
||||
{ id: 'contains', label: 'Contains (case-insensitive)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'deleteMode',
|
||||
type: 'dropdown',
|
||||
label: 'Delete',
|
||||
default: 'first',
|
||||
choices: [
|
||||
{ id: 'first', label: 'First match only' },
|
||||
{ id: 'all', label: 'All matches' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'onlyMine',
|
||||
type: 'checkbox',
|
||||
label: 'Only delete cues created by this user',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
callback: async (evt) => {
|
||||
const cueName = await self.parseVariablesInString(String(evt.options.cueName ?? ''));
|
||||
const matchMode = String(evt.options.matchMode ?? 'exact');
|
||||
const deleteMode = String(evt.options.deleteMode ?? 'first');
|
||||
const onlyMine = Boolean(evt.options.onlyMine);
|
||||
await self.deleteCueByName({
|
||||
name: cueName,
|
||||
matchMode,
|
||||
deleteMode,
|
||||
onlyMine,
|
||||
});
|
||||
},
|
||||
},
|
||||
select_decoder: {
|
||||
name: 'Select decoder/player',
|
||||
options: [
|
||||
{
|
||||
id: 'playerId',
|
||||
type: 'dropdown',
|
||||
label: 'Decoder / Player',
|
||||
default: '',
|
||||
choices: self.getPlayerChoices(),
|
||||
},
|
||||
],
|
||||
callback: async (evt) => {
|
||||
await self.selectPlayer(String(evt.options.playerId || ''));
|
||||
},
|
||||
},
|
||||
resync_to_cue: {
|
||||
name: 'Resync playhead to cue (use on the button that fires it)',
|
||||
options: [
|
||||
{
|
||||
id: 'cueName',
|
||||
type: 'textinput',
|
||||
label: 'Cue name',
|
||||
default: '',
|
||||
useVariables: true,
|
||||
},
|
||||
],
|
||||
callback: async (evt) => {
|
||||
const cueName = await self.parseVariablesInString(String(evt.options.cueName ?? ''));
|
||||
self.resyncToCue(cueName);
|
||||
},
|
||||
},
|
||||
resync_to_next_cue: {
|
||||
name: 'Resync playhead to the NEXT cue',
|
||||
options: [],
|
||||
callback: async () => {
|
||||
self.resyncToNextCue();
|
||||
},
|
||||
},
|
||||
lookup_cue_by_name: {
|
||||
name: 'Lookup cue by name (stores in lookup_* variables)',
|
||||
options: [
|
||||
{
|
||||
id: 'cueName',
|
||||
type: 'textinput',
|
||||
label: 'Cue name',
|
||||
default: '',
|
||||
useVariables: true,
|
||||
},
|
||||
{
|
||||
id: 'matchMode',
|
||||
type: 'dropdown',
|
||||
label: 'Match mode',
|
||||
default: 'exact',
|
||||
choices: [
|
||||
{ id: 'exact', label: 'Exact (case-insensitive)' },
|
||||
{ id: 'contains', label: 'Contains (case-insensitive)' },
|
||||
],
|
||||
},
|
||||
],
|
||||
callback: async (evt) => {
|
||||
const cueName = await self.parseVariablesInString(String(evt.options.cueName ?? ''));
|
||||
const mode = String(evt.options.matchMode ?? 'exact');
|
||||
self.lookupCueByName(cueName, mode);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=actions.js.map
|
||||
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GetConfigFields = GetConfigFields;
|
||||
function GetConfigFields() {
|
||||
return [
|
||||
{
|
||||
type: 'textinput',
|
||||
id: 'username',
|
||||
label: 'Username / Email',
|
||||
width: 12,
|
||||
},
|
||||
{
|
||||
type: 'dropdown',
|
||||
id: 'defaultPlayerHardwareId',
|
||||
label: 'Default decoder/player on startup',
|
||||
width: 12,
|
||||
default: '',
|
||||
choices: [{ id: '', label: '(none)' }],
|
||||
},
|
||||
// NEW: polling intervals
|
||||
{
|
||||
type: 'number',
|
||||
id: 'playerPollMs',
|
||||
label: 'Player position poll (ms)',
|
||||
width: 6,
|
||||
min: 250,
|
||||
max: 30000,
|
||||
default: 1000,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
id: 'positionTrimMs',
|
||||
label: 'Position trim (ms) - only if it still reads behind',
|
||||
width: 6,
|
||||
min: -30000,
|
||||
max: 30000,
|
||||
default: 0,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
id: 'cuePollMs',
|
||||
label: 'Cue list refresh (ms)',
|
||||
width: 6,
|
||||
min: 1000,
|
||||
max: 60000,
|
||||
default: 5000,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
id: 'listPollMs',
|
||||
label: 'User & decoder inf0 poll (ms)',
|
||||
width: 6,
|
||||
min: 10000,
|
||||
max: 3600000,
|
||||
default: 120000,
|
||||
},
|
||||
{
|
||||
type: 'secret-text',
|
||||
id: 'password',
|
||||
label: 'Password',
|
||||
width: 12,
|
||||
}
|
||||
];
|
||||
}
|
||||
//# sourceMappingURL=config.js.map
|
||||
Vendored
+113
@@ -0,0 +1,113 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UpdateFeedbacks = UpdateFeedbacks;
|
||||
const base_1 = require("@companion-module/base");
|
||||
function UpdateFeedbacks(self) {
|
||||
self.setFeedbackDefinitions({
|
||||
resi_connected: {
|
||||
name: 'Resi: Connected (auth OK)',
|
||||
type: 'boolean',
|
||||
defaultStyle: {
|
||||
bgcolor: (0, base_1.combineRgb)(0, 200, 0),
|
||||
color: (0, base_1.combineRgb)(0, 0, 0),
|
||||
},
|
||||
options: [],
|
||||
callback: () => {
|
||||
const v = self.getVariableValue('auth_status') ?? '';
|
||||
return v === 'OK';
|
||||
},
|
||||
},
|
||||
resi_error: {
|
||||
name: 'Resi: Error (auth/API)',
|
||||
type: 'boolean',
|
||||
defaultStyle: {
|
||||
bgcolor: (0, base_1.combineRgb)(200, 0, 0),
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
},
|
||||
options: [],
|
||||
callback: () => {
|
||||
const auth = (self.getVariableValue('auth_status') ?? '').trim();
|
||||
const err = (self.getVariableValue('last_error') ?? '').trim();
|
||||
// True when we have an error message OR auth isn't OK (but ignore "Missing credentials" if you prefer)
|
||||
return err.length > 0 || (auth.length > 0 && auth !== 'OK');
|
||||
},
|
||||
},
|
||||
player_active: {
|
||||
name: 'Resi: Player active (position > 0s)',
|
||||
type: 'boolean',
|
||||
defaultStyle: {
|
||||
bgcolor: (0, base_1.combineRgb)(0, 120, 255),
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
},
|
||||
options: [],
|
||||
callback: () => {
|
||||
const s = self.getVariableValue('current_position_sec') ?? '0';
|
||||
const n = Number(s);
|
||||
return Number.isFinite(n) && n > 0;
|
||||
},
|
||||
},
|
||||
approaching_next_cue: {
|
||||
name: 'Resi: Approaching next cue (<= threshold seconds)',
|
||||
type: 'boolean',
|
||||
defaultStyle: {
|
||||
bgcolor: (0, base_1.combineRgb)(255, 180, 0),
|
||||
color: (0, base_1.combineRgb)(0, 0, 0),
|
||||
},
|
||||
options: [
|
||||
{
|
||||
id: 'threshold',
|
||||
type: 'number',
|
||||
label: 'Threshold (seconds)',
|
||||
default: 30,
|
||||
min: 1,
|
||||
max: 3600,
|
||||
},
|
||||
],
|
||||
callback: (fb) => {
|
||||
const nextName = (self.getVariableValue('next_cue_name') ?? '').trim();
|
||||
if (!nextName)
|
||||
return false;
|
||||
const s = self.getVariableValue('next_cue_time_in_seconds') ?? '';
|
||||
const n = Number(s);
|
||||
if (!Number.isFinite(n))
|
||||
return false;
|
||||
const thr = Number(fb.options.threshold);
|
||||
return Number.isFinite(thr) ? n <= thr : false;
|
||||
},
|
||||
},
|
||||
playback_frozen: {
|
||||
name: 'Resi: Playback frozen (position not changing)',
|
||||
type: 'boolean',
|
||||
defaultStyle: {
|
||||
bgcolor: (0, base_1.combineRgb)(200, 0, 0),
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
},
|
||||
options: [
|
||||
{
|
||||
id: 'staleSeconds',
|
||||
type: 'number',
|
||||
label: 'Frozen if unchanged for (seconds)',
|
||||
default: 6,
|
||||
min: 2,
|
||||
max: 120,
|
||||
},
|
||||
],
|
||||
callback: (fb) => {
|
||||
const staleSeconds = Number(fb.options.staleSeconds) || 6;
|
||||
const curStr = self.getVariableValue('current_position_sec') ?? '';
|
||||
const cur = Number(curStr);
|
||||
if (!Number.isFinite(cur))
|
||||
return false;
|
||||
const now = Date.now();
|
||||
const lastStr = self.getVariableValue('playback_last_change_epoch_ms') ?? '';
|
||||
const last = Number(lastStr);
|
||||
// If you haven't implemented playback_last_change_epoch_ms yet, we can't detect freezing reliably.
|
||||
if (!Number.isFinite(last) || last <= 0)
|
||||
return false;
|
||||
const ageSec = (now - last) / 1000;
|
||||
return ageSec >= staleSeconds;
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=feedbacks.js.map
|
||||
Vendored
+1076
File diff suppressed because it is too large
Load Diff
Vendored
+303
@@ -0,0 +1,303 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UpdatePresets = UpdatePresets;
|
||||
const base_1 = require("@companion-module/base");
|
||||
function UpdatePresets(self) {
|
||||
const presets = {};
|
||||
// Helper to reference THIS instance’s variables in button text
|
||||
// Companion variable syntax uses the instance label.
|
||||
const L = self.label;
|
||||
// ----------------------------
|
||||
// STATUS / HEALTH
|
||||
// ----------------------------
|
||||
presets['status_connected'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Status',
|
||||
name: 'Connected (green) / Error (red)',
|
||||
style: {
|
||||
text: `Resi\n$(${L}:auth_status)`,
|
||||
size: '14',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(40, 40, 40),
|
||||
show_topbar: true,
|
||||
},
|
||||
steps: [],
|
||||
feedbacks: [
|
||||
{
|
||||
feedbackId: 'resi_connected',
|
||||
options: {},
|
||||
},
|
||||
{
|
||||
feedbackId: 'resi_error',
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
presets['status_player_active'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Status',
|
||||
name: 'Player Active',
|
||||
style: {
|
||||
text: `Player\n$(${L}:current_position)`,
|
||||
size: '14',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(40, 40, 40),
|
||||
show_topbar: true,
|
||||
},
|
||||
steps: [],
|
||||
feedbacks: [
|
||||
{
|
||||
feedbackId: 'player_active',
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
presets['status_frozen'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Status',
|
||||
name: 'Playback Frozen Warning',
|
||||
style: {
|
||||
text: `FROZEN?\n$(${L}:current_position)`,
|
||||
size: '14',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(40, 40, 40),
|
||||
show_topbar: true,
|
||||
},
|
||||
steps: [],
|
||||
feedbacks: [
|
||||
{
|
||||
feedbackId: 'playback_frozen',
|
||||
options: {
|
||||
staleSeconds: 6,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
// ----------------------------
|
||||
// CUE DISPLAY (NO ACTIONS)
|
||||
// ----------------------------
|
||||
presets['cue_next_display'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Cues',
|
||||
name: 'Next Cue Display',
|
||||
style: {
|
||||
text: `NEXT:\n$(${L}:next_cue_name)\nT- $(${L}:next_cue_time_in_hms)`,
|
||||
size: '14',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(0, 0, 0),
|
||||
show_topbar: false,
|
||||
},
|
||||
steps: [],
|
||||
feedbacks: [
|
||||
{
|
||||
feedbackId: 'approaching_next_cue',
|
||||
options: { threshold: 30 },
|
||||
},
|
||||
],
|
||||
};
|
||||
presets['cue_prev_display'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Cues',
|
||||
name: 'Previous Cue Display',
|
||||
style: {
|
||||
text: `PREV:\n$(${L}:prev_cue_name)\n+ $(${L}:prev_cue_time_ago_in_hms)`,
|
||||
size: '14',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(0, 0, 0),
|
||||
show_topbar: false,
|
||||
},
|
||||
steps: [],
|
||||
feedbacks: [],
|
||||
};
|
||||
// ----------------------------
|
||||
// MAINTENANCE / REFRESH
|
||||
// ----------------------------
|
||||
presets['refresh_profile'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Maintenance',
|
||||
name: 'Refresh Profile',
|
||||
style: {
|
||||
text: 'Refresh\nProfile',
|
||||
size: '18',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(60, 60, 60),
|
||||
show_topbar: false,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
down: [
|
||||
{
|
||||
actionId: 'refresh_profile',
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
up: [],
|
||||
},
|
||||
],
|
||||
feedbacks: [
|
||||
{ feedbackId: 'resi_connected', options: {} },
|
||||
{ feedbackId: 'resi_error', options: {} },
|
||||
],
|
||||
};
|
||||
presets['refresh_lists'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Maintenance',
|
||||
name: 'Refresh Venues/Players',
|
||||
style: {
|
||||
text: 'Refresh\nLists',
|
||||
size: '18',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(60, 60, 60),
|
||||
show_topbar: false,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
down: [
|
||||
{
|
||||
actionId: 'refresh_lists',
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
up: [],
|
||||
},
|
||||
],
|
||||
feedbacks: [
|
||||
{ feedbackId: 'resi_connected', options: {} },
|
||||
{ feedbackId: 'resi_error', options: {} },
|
||||
],
|
||||
};
|
||||
// ----------------------------
|
||||
// CUE ACTIONS
|
||||
// ----------------------------
|
||||
presets['add_cue_player'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Cues',
|
||||
name: 'Add Cue @ Player Position',
|
||||
style: {
|
||||
text: 'Add Cue\n@ Player',
|
||||
size: '18',
|
||||
color: (0, base_1.combineRgb)(0, 0, 0),
|
||||
bgcolor: (0, base_1.combineRgb)(180, 180, 180),
|
||||
show_topbar: false,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
down: [
|
||||
{
|
||||
actionId: 'add_cue_at_position',
|
||||
options: {
|
||||
name: 'Web Cue',
|
||||
privateCue: false,
|
||||
positionMode: 'player',
|
||||
customPosition: '00:00:00.000',
|
||||
},
|
||||
},
|
||||
],
|
||||
up: [],
|
||||
},
|
||||
],
|
||||
feedbacks: [
|
||||
{ feedbackId: 'resi_connected', options: {} },
|
||||
{ feedbackId: 'resi_error', options: {} },
|
||||
],
|
||||
};
|
||||
presets['add_cue_custom'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Cues',
|
||||
name: 'Add Cue @ Custom Time',
|
||||
style: {
|
||||
text: 'Add Cue\n@ Custom',
|
||||
size: '18',
|
||||
color: (0, base_1.combineRgb)(0, 0, 0),
|
||||
bgcolor: (0, base_1.combineRgb)(180, 180, 180),
|
||||
show_topbar: false,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
down: [
|
||||
{
|
||||
actionId: 'add_cue_at_position',
|
||||
options: {
|
||||
// user can edit this preset, OR replace with a variable
|
||||
name: 'Custom Cue',
|
||||
privateCue: false,
|
||||
positionMode: 'custom',
|
||||
customPosition: '00:10:00.000',
|
||||
},
|
||||
},
|
||||
],
|
||||
up: [],
|
||||
},
|
||||
],
|
||||
feedbacks: [
|
||||
{ feedbackId: 'resi_connected', options: {} },
|
||||
{ feedbackId: 'resi_error', options: {} },
|
||||
],
|
||||
};
|
||||
presets['lookup_cue'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Cues',
|
||||
name: 'Lookup Cue (stores in lookup_* vars)',
|
||||
style: {
|
||||
text: 'Lookup\nCue',
|
||||
size: '18',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(0, 110, 200),
|
||||
show_topbar: false,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
down: [
|
||||
{
|
||||
actionId: 'lookup_cue_by_name',
|
||||
options: {
|
||||
// user edits this preset to their cue name or uses variables
|
||||
cueName: 'sermon start',
|
||||
matchMode: 'exact',
|
||||
},
|
||||
},
|
||||
],
|
||||
up: [],
|
||||
},
|
||||
],
|
||||
feedbacks: [
|
||||
{ feedbackId: 'resi_connected', options: {} },
|
||||
{ feedbackId: 'resi_error', options: {} },
|
||||
],
|
||||
};
|
||||
presets['delete_cue'] = {
|
||||
type: 'button',
|
||||
category: 'Resi • Cues',
|
||||
name: 'Delete Cue by Name',
|
||||
style: {
|
||||
text: 'DELETE\nCue',
|
||||
size: '18',
|
||||
color: (0, base_1.combineRgb)(255, 255, 255),
|
||||
bgcolor: (0, base_1.combineRgb)(140, 0, 0),
|
||||
show_topbar: false,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
down: [
|
||||
{
|
||||
actionId: 'delete_cue_by_name',
|
||||
options: {
|
||||
// user edits this preset to the cue they want to delete
|
||||
cueName: 'Companion Test',
|
||||
matchMode: 'exact',
|
||||
deleteMode: 'first',
|
||||
onlyMine: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
up: [],
|
||||
},
|
||||
],
|
||||
feedbacks: [
|
||||
{ feedbackId: 'resi_connected', options: {} },
|
||||
{ feedbackId: 'resi_error', options: {} },
|
||||
],
|
||||
};
|
||||
self.setPresetDefinitions(presets);
|
||||
}
|
||||
//# sourceMappingURL=presets.js.map
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UpgradeScripts = void 0;
|
||||
exports.UpgradeScripts = [];
|
||||
//# sourceMappingURL=upgrades.js.map
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UpdateVariableDefinitions = UpdateVariableDefinitions;
|
||||
function UpdateVariableDefinitions(instance) {
|
||||
const vars = [
|
||||
{ variableId: 'org_name', name: 'Organization name' },
|
||||
{ variableId: 'first_name', name: 'First name' },
|
||||
{ variableId: 'last_name', name: 'Last name' },
|
||||
{ variableId: 'email', name: 'Email' },
|
||||
{ variableId: 'auth_status', name: 'Auth status' },
|
||||
{ variableId: 'last_error', name: 'Last error' },
|
||||
{ variableId: 'last_poll', name: 'Last poll (local time)' },
|
||||
{ variableId: 'current_position', name: 'Current position (HH:MM:SS.ms)' },
|
||||
{ variableId: 'playback_state', name: 'Playback state (PLAYING/PAUSED)' },
|
||||
{ variableId: 'position_update_interval_sec', name: 'Seconds between Resi position updates' },
|
||||
{ variableId: 'player_fields', name: 'Fields Resi returns for this decoder (diagnostic)' },
|
||||
{ variableId: 'current_position_sec', name: 'Current position (seconds)' },
|
||||
{ variableId: 'current_event_id', name: 'Current event id (live from player)' },
|
||||
{ variableId: 'cue_count', name: 'Cue count' },
|
||||
{ variableId: 'cue_names', name: 'Cue names (comma separated)' },
|
||||
{ variableId: 'event_id', name: 'Current event id (cues loaded for)' },
|
||||
{ variableId: 'next_cue_name', name: 'Next cue name' },
|
||||
{ variableId: 'next_cue_time_in_seconds', name: 'Seconds until next cue' },
|
||||
{ variableId: 'next_cue_position', name: 'Next cue position (HH:MM:SS.ms)' },
|
||||
{ variableId: 'next_cue_tod', name: 'Next cue time-of-day (24 hour)' },
|
||||
{ variableId: 'prev_cue_name', name: 'Previous cue name' },
|
||||
{ variableId: 'prev_cue_time_ago_in_seconds', name: 'Seconds since previous cue' },
|
||||
{ variableId: 'prev_cue_position', name: 'Previous cue position (HH:MM:SS.ms)' },
|
||||
{ variableId: 'prev_cue_tod', name: 'Previous cue time-of-day (24 hour)' },
|
||||
{ variableId: 'next_cue_time_in_hms', name: 'Time until next cue (HH:MM:SS)' },
|
||||
{ variableId: 'prev_cue_time_ago_in_hms', name: 'Time since previous cue (HH:MM:SS)' },
|
||||
{ variableId: 'lookup_cue_query', name: 'Lookup cue query (name)' },
|
||||
{ variableId: 'lookup_cue_found', name: 'Lookup cue found (true/false)' },
|
||||
{ variableId: 'lookup_cue_name', name: 'Lookup cue name (matched)' },
|
||||
{ variableId: 'lookup_cue_uuid', name: 'Lookup cue uuid' },
|
||||
{ variableId: 'lookup_cue_position', name: 'Lookup cue position (HH:MM:SS.ms)' },
|
||||
{ variableId: 'lookup_cue_position_sec', name: 'Lookup cue position (seconds)' },
|
||||
{ variableId: 'lookup_cue_tod', name: 'Lookup cue time-of-day' },
|
||||
{ variableId: 'playback_last_change_epoch_ms', name: 'Playback last position change (epoch ms)' },
|
||||
];
|
||||
instance.setVariableDefinitions(vars);
|
||||
}
|
||||
//# sourceMappingURL=variables.js.map
|
||||
Reference in New Issue
Block a user