Skip to content

Commit

Permalink
reorganize vmix api, add more custom functions, add compare button fo…
Browse files Browse the repository at this point in the history
…r web vmix
  • Loading branch information
AlexFreik committed Nov 30, 2024
1 parent 9ea66b1 commit 09639d4
Show file tree
Hide file tree
Showing 10 changed files with 367 additions and 172 deletions.
2 changes: 1 addition & 1 deletion vmix-master/box.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function createBox(name, host, num) {
<span class="box-number badge badge-neutral cursor-grab w-7">${num}</span>
<input type="text" placeholder="Name" value="${name}" class="name-input input input-bordered input-xs w-24">
<input type="text" placeholder="Host" value="${host}" class="host-input input input-xs input-bordered flex-1 min-w-5">
<button class="close-btn btn btn-xs btn-error btn-outline w-[24px] p-0">
<button class="close-btn focus btn btn-xs btn-error btn-outline w-[24px] p-0">
<svg class="fill-current w-4 h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>
</button>
</div>
Expand Down
186 changes: 186 additions & 0 deletions vmix-master/compare-vmix-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
function extractLeadingNumbers(input) {
const match = input.match(/^(Virtual - )?(\d+_(?:\d+_)?)/);
if (match) {
return match[1] ? match[1] + match[2] : match[2];
}
return '';
}

function getError(type, msg) {
return `<div><span class="text-error font-semibold">Error: ${type}.</span> ${msg}</div>`;
}

function getWarning(type, msg) {
return `<div><span class="text-warning font-semibold">Warning: ${type}.</span> ${msg}</div>`;
}

function similarTypes(type1, type2) {
const audioTypes = ['Audio', 'VideoCall'];
if (audioTypes.includes(type1) && audioTypes.includes(type2)) {
return true;
}
return type1 === type2;
}

function compareSlaves() {
const className = 'border rounded-box p-3 mb-5 w-[750px] max-w-[750px] mx-auto ';
const compareReport = document.getElementById('compare-report');
const infoMsg = `
Please specify the master and slaves. The script will do the following:
<ul>
<li>Compare first slave's buss settings with others (<code>mute</code>, <code>sendToMaster</code>).</li>
<li>Compare first slave's vMix Call settings with others
(<code>callVideoSource</code>, <code>callAudioSource</code>).</li>
<li>Compare master input <b>leading numbers</b> with slaves.</li>
<li>Compare master input <b>types</b> with slaves (only for inputs with a leading number).</li>
<li>Compare master input <b>layers</b> with slaves (only for inputs with a leading number).</li>
</ul>
<b>P.S.</b> Leading number means something like <code>05_</code>, <code>05_3_</code>
or <code>05_03</code>.`;
const master = getMaster();
if (master === null) {
compareReport.className = className + 'border-info prose';
compareReport.innerHTML = infoMsg;
return;
}

const slaves = getSlaves();
slaves.unshift(master);
const nums = removeDuplicates(slaves);
if (nums.length === 1) {
compareReport.className = className + 'border-info prose';
compareReport.innerHTML = infoMsg;
return;
}

const infos = [];
for (let i = 0; i < nums.length; i++) {
const number = nums[i];
const info = getVmixInfo(number)?.value;
if (info === undefined || info === null) {
compareReport.className = className + 'border-info';
compareReport.innerHTML = `Could not fetch status for vMix #${number}.`;
return;
}
infos.push(info);
}

let innerHtml = '';
let msg = '';
const warnings = [];
const errors = [];

// check buses
Object.entries(infos[1].audio).forEach(([k, v1]) => {
for (let j = 2; j < infos.length; j++) {
const v2 = infos[j].audio[k];
const num2 = nums[j];
if (v2 === undefined) {
errors.push(getError('missing bus', `vMix #${num2} bus ${k} is disabled.`));
continue;
}

if (v1.muted !== v2.muted) {
msg = `vMix #${num2} bus ${k} is ${v2.muted === 'True' ? '' : 'not '}muted.`;
errors.push(getError('bus muted', msg));
}
if (v1.sendToMaster !== v2.sendToMaster) {
msg = `vMix #${num2} bus ${k} sendToMaster is ${v2.muted === 'True' ? '' : 'not '}enabled.`;
errors.push(getError('bus sendToMaster', msg));
}
}
});

// check vMix call settings
infos[1].inputs
.filter((input) => input.type === 'VideoCall')
.forEach((input1) => {
for (let j = 2; j < infos.length; j++) {
const input2 = infos[j].inputs[input1.number];
const num2 = nums[j];
if (input2.type !== 'VideoCall') {
continue;
}
if (input1.callVideoSource !== input2.callVideoSource) {
msg = `vMix #${num2} VideoCall input ${input1.number} video source is
"${input2.callVideoSource}" instead of "${input1.callVideoSource}".`;
errors.push(getError('vMix call settings', msg));
}
if (input1.callAudioSource !== input2.callAudioSource) {
msg = `vMix #${num2} VideoCall input ${input1.number} audio source is
"${input2.callAudioSource}" instead of "${input1.callAudioSource}".`;
errors.push(getError('vMix call settings', msg));
}
}
});

infos[0].inputs.forEach((input1, i) => {
const leadNum1 = extractLeadingNumbers(input1.title);
for (let j = 1; j < infos.length; j++) {
const input2 = infos[j].inputs[i];
const num2 = nums[j];

// check missing inputs
if (input2 === undefined) {
if (leadNum1 !== '') {
errors.push(getError('missing input', `vMix #${num2} input ${i} is missing.`));
}
continue;
}

// check leading numbers (e.g. 05_)
const leadNum2 = extractLeadingNumbers(input2.title);
if (leadNum1 !== leadNum2) {
msg = `vMix #${num2} input ${i} starts with "${leadNum2}" instead of "${leadNum1}".`;
errors.push(getError('order mismatch', msg));
}

if (leadNum1 === '') {
continue;
}

// check input types
if (!similarTypes(input1.type, input2.type)) {
msg = `vMix #${num2} input ${i} has type "${input2.type}" instead of "${input1.type}".`;
warnings.push(getWarning('type mismatch', msg));
}

// check layers
input1.overlays.forEach((over1, k) => {
const overInput1 = infos[0].inputs[over1.number];
const overLeadNum = extractLeadingNumbers(overInput1.title);
const over2 = input2.overlays[k];
if (overLeadNum === '') {
return;
} else if (over2 === undefined) {
msg = `vMix #${num2} input ${i} layer ${over1.index + 1} is missing.`;
errors.push(getError('missing layer', msg));
} else if (over1.index !== over2.index || over1.number !== over2.number) {
msg = `vMix #${num2} input ${i} layer ${over2.index + 1} <${over2.number}>
do not match master layer ${over1.index} <${over1.number}>.`;
errors.push(getError('layer mismatch', msg));
}
});
}
});

if (errors.length > 0) {
compareReport.className = className + 'border-error';
innerHtml += errors.join('');
} else if (warnings.length > 0) {
compareReport.className = className + 'border-warning';
} else {
compareReport.className = className + 'border-success';
innerHtml += 'Looks good 👍';
}
if (warnings.length > 0) {
innerHtml += '<div class="divider"></div>' + warnings.join('');
}
compareReport.innerHTML = innerHtml;
}

function hideCompareReport() {
const compareReport = document.getElementById('compare-report');
compareReport.className = 'hidden';
}
22 changes: 19 additions & 3 deletions vmix-master/custom-functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ function renderCustomFunctions() {
min: '1',
max: '100',
};
const gain = {
name: 'gain',
type: 'number',
placeholder: '0-24',
value: '',
width: 'w-14',
min: '0',
max: '24',
};
const volume = {
name: 'volume',
type: 'number',
Expand Down Expand Up @@ -59,12 +68,19 @@ function renderCustomFunctions() {
{ func: 'Stinger1', inputs: [input] },
{ func: 'Fade', inputs: [input] },
{ func: 'Cut', inputs: [input] },
{ func: 'SetGain', inputs: [gain, input] },
{ func: 'SetVolumeFade', inputs: [volume, ms, input] },
{ func: 'SetMasterVolume', inputs: [volume] },
{ func: 'SetBusAVolume', inputs: [volume] },
{ func: 'SetBusBVolume', inputs: [volume] },
{ func: 'AudioOn', inputs: [input] },
{ func: 'AudioOff', inputs: [input] },
{ func: 'MasterAudioOn', inputs: [] },
{ func: 'MasterAudioOff', inputs: [] },
{ func: 'BusAAudioOn', inputs: [] },
{ func: 'BusAAudioOff', inputs: [] },
{ func: 'BusBAudioOn', inputs: [] },
{ func: 'BusBAudioOff', inputs: [] },
{ func: 'SetPosition', inputs: [min, sec, input] },
{ func: 'StartCountdown', inputs: [input] },
{ func: 'StopCountdown', inputs: [input] },
Expand Down Expand Up @@ -108,7 +124,7 @@ function renderCustomFunctions() {
btn.onclick = () => {
const container = btn.parentElement.parentElement;
const inputParam = container.querySelector('.input-param');
const valueParam = container.querySelector('.value-param');
const gainParam = container.querySelector('.gain-param');
const volumeParam = container.querySelector('.volume-param');
const minParam = container.querySelector('.min-param');
const secParam = container.querySelector('.sec-param');
Expand All @@ -118,8 +134,8 @@ function renderCustomFunctions() {
if (inputParam?.value) {
request += '&Input=' + inputParam.value;
}
if (valueParam?.value) {
request += '&Value=' + valueParam.value;
if (gainParam?.value) {
request += '&Value=' + gainParam.value;
}
if (volumeParam?.value && msParam?.value) {
request += '&Value=' + volumeParam.value + ',' + msParam.value;
Expand Down
12 changes: 6 additions & 6 deletions vmix-master/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
<link rel="stylesheet" href="./output.css" />
<script src="./Sortable.min.js" defer></script>
<script src="./tools.js" defer></script>
<script src="./vmix-api.js" defer></script>
<script src="./box.js" defer></script>
<script src="./vmix-info.js" defer></script>
<script src="./vmix-web.js" defer></script>
<script src="./compare-vmix-info.js" defer></script>
<script src="./custom-functions.js" defer></script>
<script src="./script.js" defer></script>
</head>
Expand Down Expand Up @@ -53,8 +55,12 @@
<input id="master" type="number" min="1" name="master" placeholder="e.g. 1" class="url-param input input-xs input-bordered w-16" />
<span class="text-md ms-3">Slaves:</span>
<input id="slaves" type="text" name="slaves" placeholder="e.g. 2 3" class="url-param input input-xs input-bordered flex-1" />
<button class="btn btn-secondary btn-xs ms-3" onclick="compareSlaves()">Compare</button>
<button class="btn btn-secondary btn-xs" onclick="hideCompareReport()">Hide</button>
</div>

<div id="compare-report"></div>

<div id="vmix-container" class="vmix-web pb-5">
<div
id="vmix-screens"
Expand Down Expand Up @@ -139,12 +145,6 @@
<div class="badge swap-off badge-secondary badge-outline rounded py-3">custom</div>
</label>

<label class="swap m-1">
<input id="view-mode" type="checkbox" class="url-param" checked />
<div class="badge swap-on badge-secondary rounded py-3">control</div>
<div class="badge swap-off badge-secondary badge-outline rounded py-3">control</div>
</label>

<label class="swap m-1">
<input id="show-focus" type="checkbox" class="show-toggle url-param" checked />
<div class="badge swap-on badge-secondary badge-outline rounded py-3">focus</div>
Expand Down
20 changes: 20 additions & 0 deletions vmix-master/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -5292,6 +5292,11 @@ html:has(.drawer-toggle:checked) {
border-color: var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)));
}

.border-info {
--tw-border-opacity: 1;
border-color: var(--fallback-in,oklch(var(--in)/var(--tw-border-opacity)));
}

.border-neutral {
--tw-border-opacity: 1;
border-color: var(--fallback-n,oklch(var(--n)/var(--tw-border-opacity)));
Expand All @@ -5307,6 +5312,16 @@ html:has(.drawer-toggle:checked) {
border-color: var(--fallback-s,oklch(var(--s)/var(--tw-border-opacity)));
}

.border-success {
--tw-border-opacity: 1;
border-color: var(--fallback-su,oklch(var(--su)/var(--tw-border-opacity)));
}

.border-warning {
--tw-border-opacity: 1;
border-color: var(--fallback-wa,oklch(var(--wa)/var(--tw-border-opacity)));
}

.bg-base-100 {
--tw-bg-opacity: 1;
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
Expand Down Expand Up @@ -5560,6 +5575,11 @@ html:has(.drawer-toggle:checked) {
color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)));
}

.text-warning {
--tw-text-opacity: 1;
color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)));
}

.opacity-100 {
opacity: 1;
}
Expand Down
27 changes: 0 additions & 27 deletions vmix-master/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,6 @@ async function refreshInstances(cnt = 0) {
requestAnimationFrame(() => refreshInstances((cnt + 1) % refreshRate));
}

function setVmixButtons() {
const disabled = !document.getElementById('view-mode').checked;
document
.querySelector('.custom-functions-container')
.querySelectorAll('button')
.forEach((btn) => {
if (disabled) {
btn.disabled = true;
} else {
btn.removeAttribute('disabled');
}
});
document
.getElementById('vmix-container')
.querySelectorAll('button')
.forEach((btn) => {
if (disabled) {
btn.disabled = true;
} else {
btn.removeAttribute('disabled');
}
});
}

function showElements() {
document.querySelectorAll('.show-toggle').forEach((elem) => {
const name = elem.id.slice('show-'.length);
Expand Down Expand Up @@ -95,7 +71,6 @@ const vmixInfos = [];
renderCustomFunctions();
prerenderVmixWeb();
showStoredLogs();
setVmixButtons();
showElements();

document
Expand All @@ -115,8 +90,6 @@ const vmixInfos = [];
document.getElementById('refresh-rate').addEventListener('change', updateRefreshRates);
refreshInstances();

document.getElementById('view-mode').addEventListener('click', setVmixButtons);

new Sortable(document.getElementById('boxes'), {
animation: 150,
handle: '.cursor-grab', // Draggable by the entire row
Expand Down
Loading

0 comments on commit 09639d4

Please sign in to comment.