convert to different atem library. Add FTB and DSK

This commit is contained in:
2023-02-07 00:35:12 -06:00
parent 8d70d9d246
commit bd6757c99f
6 changed files with 417 additions and 179 deletions
+113 -65
View File
@@ -1,13 +1,12 @@
const { infoUpdate } = require('../../src/device');
exports.config = {
defaultName: 'ATEM',
connectionType: 'atem',
defaultPort: 9910,
mayChangePort: false,
heartbeatInterval: 5000,
heartbeatTimeout: 6000,
// heartbeatInterval: 5000,
// heartbeatTimeout: 20000,
searchOptions: {
// TODO: actually populate search options
type: 'UDPsocket',
searchBuffer: Buffer.from('', 'ascii'),
testPort: 9910,
@@ -19,72 +18,100 @@ exports.config = {
exports.ready = function ready(_device) {
console.log('atem ready');
const device = _device;
device.data = device.connection.state;
device.draw();
// TODO: this is a little hacky but it works? gotta be better way to get initial update going
const interval = setInterval(() => {
device.update('inputs', device.data.video.mixEffects);
device.update('fadeToBlack', device.data.video.mixEffects);
device.update(`transitionPosition`, device.data.video.mixEffects);
device.update('downstreamKeyers', device.data.video.downstreamKeyers);
}, 1000);
setTimeout(() => {
clearInterval(interval);
}, 4000);
};
exports.update = function update(device, _document, updateType, data) {
const document = _document;
switch (updateType) {
case 'downstreamKeyers':
for (let i = 0; i < data.length; i++) {
const dsk = data[i];
if (updateType === 'tbar' && document.getElementById('tbar-pos')) {
document.getElementById('tbar-pos').value = data;
}
};
exports.data = function data(_device, msg) {
const device = _device;
this.deviceInfoUpdate(device, 'status', 'ok');
switch (msg.event) {
case 'sourceConfiguration':
if (device.data.source === undefined) {
device.data.source = {};
}
if (device.data.source[msg.sourceID] === undefined) {
device.data.source[msg.sourceID] = {
tally: {
preview: false,
program: false,
},
};
}
device.data.source[msg.sourceID] = {
...device.data.source[msg.sourceID],
...msg.sourceConfiguration,
};
break;
case 'sourceTally':
if (device.data.source === undefined) {
device.data.source = {};
}
if (device.data.source[msg.sourceID] === undefined) {
device.data.source[msg.sourceID] = {};
}
device.data.source[msg.sourceID].tally = msg.state;
device.draw();
break;
case 'connectionStateChange':
device.draw();
break;
case 'rawCommand':
if (msg.cmd.name === '_pin') {
if (msg.cmd.data !== undefined) {
infoUpdate(device, 'defaultName', msg.cmd.data.toString().trim());
if (dsk.isAuto) {
document.getElementById(`dsk-${i}-auto`).classList.add('atem-red');
} else {
document.getElementById(`dsk-${i}-auto`).classList.remove('atem-red');
}
} else if (msg.cmd.name === '_top') {
device.data.topology = {
MEs: msg.cmd.data[0],
sources: msg.cmd.data[1],
colorGens: msg.cmd.data[2],
AUXs: msg.cmd.data[3],
DSKs: msg.cmd.data[4],
stingers: msg.cmd.data[5],
DVEs: msg.cmd.data[6],
superSources: msg.cmd.data[7],
};
} else if (msg.cmd.name === 'TrPs') {
const tbarPos = msg.cmd.data.readUint16BE(4);
device.update('tbar', tbarPos);
if (dsk.onAir) {
document.getElementById(`dsk-${i}-onair`).classList.add('atem-red');
} else {
document.getElementById(`dsk-${i}-onair`).classList.remove('atem-red');
}
if (dsk.properties.tie) {
document.getElementById(`dsk-${i}-tie`).classList.add('atem-yellow');
} else {
document.getElementById(`dsk-${i}-tie`).classList.remove('atem-yellow');
}
if (dsk.remainingFrames === dsk.properties.rate) {
document.getElementById(`dsk-${i}-rate`).textContent = '1:00';
} else {
// TODO: format in 0:00 format
document.getElementById(`dsk-${i}-rate`).textContent = `00${dsk.remainingFrames}`.slice(-2);
}
}
break;
case 'transitionPosition':
for (let i = 0; i < data.length; i++) {
const tbarId = `me-${i}-tbar-pos`;
if (document.getElementById(tbarId)) {
document.getElementById(tbarId).value = data[i].transitionPosition.handlePosition;
}
}
break;
case 'fadeToBlack':
for (let i = 0; i < data.length; i++) {
const fadeToBlack = data[i].fadeToBlack;
const ftbRateId = `me-${i}-ftb-rate`;
const ftbId = `me-${i}-ftb`;
if (fadeToBlack.remainingFrames === fadeToBlack.rate) {
document.getElementById(ftbRateId).textContent = '1:00';
} else {
// TODO: format in 0:00 format
document.getElementById(ftbRateId).textContent = `00${fadeToBlack.remainingFrames}`.slice(-2);
}
if (fadeToBlack.inTransition || fadeToBlack.isFullyBlack) {
document.getElementById(ftbId).classList.add('atem-red');
} else {
document.getElementById(ftbId).classList.remove('atem-red');
}
}
break;
case 'inputs':
for (let i = 0; i < data.length; i++) {
const programInputs = device.connection.listVisibleInputs('program', i);
const previewInputs = device.connection.listVisibleInputs('preview', i);
Object.entries(device.data.inputs).forEach(([inputId, input]) => {
if (programInputs.includes(Number(inputId))) {
document.getElementById(`me-${i}-program-input-${inputId}`).classList.add('atem-red');
} else {
document.getElementById(`me-${i}-program-input-${inputId}`).classList.remove('atem-red');
}
if (previewInputs.includes(Number(inputId))) {
document.getElementById(`me-${i}-preview-input-${inputId}`).classList.add('atem-green');
} else {
document.getElementById(`me-${i}-preview-input-${inputId}`).classList.remove('atem-green');
}
});
}
break;
default:
@@ -92,4 +119,25 @@ exports.data = function data(_device, msg) {
}
};
exports.heartbeat = function heartbeat(device) {};
exports.data = function data(_device, msg) {
const device = _device;
this.deviceInfoUpdate(device, 'status', 'ok');
device.data = msg.state;
msg.pathToChange.forEach((path) => {
if (path.includes('transitionPosition')) {
device.update(`transitionPosition`, device.data.video.mixEffects);
} else if (path.includes('downstreamKeyers')) {
device.update('downstreamKeyers', device.data.video.downstreamKeyers);
} else if (path.includes('fadeToBlack')) {
device.update('fadeToBlack', device.data.video.mixEffects);
} else if (path.includes('programInput')) {
device.update('inputs', device.data.video.mixEffects);
} else if (path.includes('previewInput')) {
device.update('inputs', device.data.video.mixEffects);
} else {
console.log(path);
console.log(device.data);
}
});
};
+23 -2
View File
@@ -21,16 +21,21 @@ h3 {
border-radius: 5px;
}
.program-active {
.atem-red {
background: rgb(255, 130, 131);
background: radial-gradient(circle, rgba(255, 130, 131, 1) 0%, rgba(255, 41, 40, 1) 100%);
}
.preview-active {
.atem-green {
background: rgb(14, 238, 7);
background: radial-gradient(circle, rgba(14, 238, 7, 1) 0%, rgba(29, 204, 14, 1) 100%);
}
.atem-yellow {
background: rgb(255, 240, 0);
background: radial-gradient(circle, rgba(255, 240, 0, 1) 0%, rgba(250, 169, 9, 1) 100%);
}
.source-label {
display: flex;
align-items: center;
@@ -43,3 +48,19 @@ h3 {
height: 40px;
accent-color: limegreen;
}
.dsk .source-wrapper {
display: flex;
flex-direction: column;
width: fit-content;
}
.dsk-rate,
.ftb-rate {
display: flex;
justify-content: center;
}
.dsk-rate-label,
.ftb-rate-label {
color: rgb(235, 110, 0);
}
+75 -40
View File
@@ -2,48 +2,83 @@
<h1><%= listName %></h1>
</header>
<div class="program">
<h3>Program</h3>
<div class="source-wrapper">
<% Object.entries(data.source).forEach(([sourceID, state])=>{ %>
<% if (state.tally && state.tally.program) { %>
<div id="atem-program-source-<%= sourceID %>" class="atem-input program-active">
<div class="source-label">
<%= state.abbreviation %>
<% if (data.video.mixEffects) { %>
<% Object.entries(data.video.mixEffects).forEach(([meIndex, state])=>{ %>
<div class="mixeffect_wrapper" id="me-<%= meIndex %>">
<h1>ME<%= Number(meIndex) + 1 %></h1>
<div class="program">
<h3>Program</h3>
<div class="source-wrapper">
<% Object.entries(data.inputs).forEach(([inputId, state])=>{ %>
<div id="me-<%= meIndex %>-program-input-<%= inputId %>" class="atem-input">
<div class="source-label"><%= state.shortName %></div>
</div>
</div>
<% } else { %>
<div id="atem-program-source-<%= sourceID %>" class="atem-input">
<div class="source-label">
<%= state.abbreviation %>
<% }) %>
</div>
</div>
<div class="preview">
<h3>Preview</h3>
<div class="source-wrapper">
<% Object.entries(data.inputs).forEach(([inputId, state])=>{ %>
<div id="me-<%= meIndex %>-preview-input-<%= inputId %>" class="atem-input">
<div class="source-label"><%= state.shortName %></div>
</div>
</div>
<% } %>
<% }) %>
<% }) %>
</div>
</div>
<div class="next-transition">
<h3>Next Transition</h3>
<div class="source-wrapper"></div>
</div>
<div class="transition-style">
<h3>Transition Style</h3>
<div class="source-wrapper"></div>
</div>
<div class="fade-to-black">
<h3>Fade to Black</h3>
<div class="source-wrapper">
<div class="ftb-rate">
<div id="me-<%= meIndex %>-ftb-rate" class="ftb-rate-label">1:00</div>
</div>
<div id="me-<%= meIndex %>-ftb" class="atem-input">
<div class="source-label">FTB</div>
</div>
</div>
</div>
<div class="source-wrapper">
<progress id="me-<%= meIndex %>-tbar-pos" max="10000" value="0" class="tbar-progress"></progress>
</div>
</div>
</div>
<% }) %>
<% } %>
<div class="preview">
<h3>Preview</h3>
<div class="source-wrapper">
<% Object.entries(data.source).forEach(([sourceID, state])=>{ %>
<% if (state.tally && state.tally.preview) { %>
<div id="atem-preview-source-<%= sourceID %>" class="atem-input preview-active">
<div class="source-label">
<div><%= state.abbreviation %></div>
</div>
</div>
<% } else { %>
<div id="atem-preview-source-<%= sourceID %>" class="atem-input">
<div class="source-label">
<div><%= state.abbreviation %></div>
</div>
</div>
<% } %>
<% }) %>
</div>
</div>
<div class="source-wrapper">
<progress id="tbar-pos" max="10000" value="0" class="tbar-progress"></progress>
</div>
<% if (data.video.downstreamKeyers) { %>
<% Object.entries(data.video.downstreamKeyers).forEach(([dskIndex, state])=>{ %>
<div class="dsk">
<h3>DSK<%= Number(dskIndex) + 1 %></h3>
<div class="source-wrapper">
<div id="dsk-<%= dskIndex %>-tie" class="atem-input">
<div class="source-label">TIE</div>
</div>
<div class="dsk-rate">
<div id="dsk-<%= dskIndex %>-rate" class="dsk-rate-label">1:00</div>
</div>
<div id="dsk-<%= dskIndex %>-onair" class="atem-input">
<div class="source-label">ON AIR</div>
</div>
<div id="dsk-<%= dskIndex %>-auto" class="atem-input">
<div class="source-label">AUTO</div>
</div>
</div>
</div>
<% }) %>
<% } %>