I usually work with VMix projects that have several inputs with Multiwindows, (2 or 3 Windows),
with which I had the need to change the fonts shown in each layer with a certain speed.
In this forum I asked if with UTC you could make drop-down elements that take project inputs from the API,
and when selecting a certain input, it will be applied to a certain layer of the Multi-Window composition called MVWX in the project.
But they commented that UTC list selectors can only execute changes to text fields in titles.
Out of desperation of needing a solution, I expressed my problem to ChatGPT, and they gave me a solution, which after much debugging,
has given this result.
I share and explain to you.
The script for the MVW.HMTL file is as follows:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Selector Dinámico de Fuentes - vMix</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome Icons -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
font-family: Arial, sans-serif;
}
.container {
margin-top: 50px;
}
.card {
border-radius: 15px;
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
}
.card-header {
background-color: #007bff;
color: white;
text-align: center;
}
.form-label {
font-weight: bold;
}
select {
border-radius: 8px;
}
.btn-update {
background-color: #28a745;
color: white;
}
.btn-update:hover {
background-color: #218838;
}
.icon {
font-size: 1.5rem;
}
/* Estilo para la caja azul de los títulos MVW */
.mvw-title-box {
background-color: #007bff; /* Color azul */
color: white; /* Texto blanco */
padding: 10px;
border-radius: 8px;
margin-bottom: 15px;
text-align: center;
font-weight: bold;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-video icon"></i> Selector Dinámico de Fuentes MVW</h3>
</div>
<div class="card-body">
<form>
<!-- Contenedor dinámico para las entradas MVW -->
<div id="mvw-entries"></div>
<!-- Botón para actualizar manualmente las fuentes -->
<button type="button" class="btn btn-update w-100 mt-3" onclick="loadSources()">
Actualizar Fuentes
</button>
</form>
</div>
</div>
</div>
<!-- Bootstrap JS and dependencies -->
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
<script>
const vMixAPI = "http://192.168.1.5:8088/api/";
let selectedSources = {};
async function loadSources() {
try {
console.log("Cargando fuentes desde vMix...");
let response = await fetch(vMixAPI);
let text = await response.text();
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(text, "text/xml");
let inputs = xmlDoc.getElementsByTagName("input");
let mvwEntries = {};
Array.from(inputs).forEach(input => {
let title = input.getAttribute("title");
if (title && title.startsWith("MVW")) {
let mvwIndex = title.replace("MVW", "");
let mvwEntry = { layers: ["", "", ""] };
let overlayAttr = input.getElementsByTagName("overlay");
for (let j = 0; j < overlayAttr.length; j++) {
let layerIndex = overlayAttr[j].getAttribute("index");
let key = overlayAttr[j].getAttribute("key");
mvwEntry.layers[layerIndex] = key;
}
mvwEntries[mvwIndex] = mvwEntry;
}
});
selectedSources = mvwEntries;
let mvwContainer = document.getElementById("mvw-entries");
mvwContainer.innerHTML = "";
Object.keys(mvwEntries).forEach(mvwIndex => {
let entry = mvwEntries[mvwIndex];
let mvwDiv = document.createElement("div");
mvwDiv.classList.add("mb-4");
mvwDiv.innerHTML = `<div class="mvw-title-box">Entrada MVW${mvwIndex}</div>`;
["layer1", "layer2", "layer3"].forEach((layer, layerIndex) => {
let label = document.createElement("label");
label.classList.add("form-label");
label.setAttribute("for", `${layer}-${mvwIndex}`);
label.innerText = `Ventana ${layerIndex + 1}:`;
let select = document.createElement("select");
select.classList.add("form-select");
select.id = `${layer}-${mvwIndex}`;
select.onchange = () => updateLayer(mvwIndex, layerIndex);
for (let i = 0; i < inputs.length; i++) {
let option = document.createElement("option");
let name = inputs[i].getAttribute("title");
let key = inputs[i].getAttribute("key");
option.value = key;
option.textContent = name;
select.appendChild(option);
}
select.value = entry.layers[layerIndex] || "";
mvwDiv.appendChild(label);
mvwDiv.appendChild(select);
});
mvwContainer.appendChild(mvwDiv);
});
} catch (error) {
console.error("Error al cargar fuentes:", error);
alert("Error al conectarse con vMix.");
}
}
async function updateLayer(mvwIndex, layerIndex) {
let selectedKey = document.getElementById(`layer${layerIndex + 1}-${mvwIndex}`).value;
if (!selectedKey) return;
try {
let url = `${vMixAPI}?Function=SetMultiViewOverlay&Input=MVW${mvwIndex}&Value=${layerIndex + 1},${selectedKey}`;
console.log(`Cambiando Capa ${layerIndex + 1} de MVW${mvwIndex} a ${selectedKey}`);
await fetch(url);
selectedSources[mvwIndex].layers[layerIndex] = selectedKey;
} catch (error) {
console.error("Error al actualizar Multiview:", error);
}
}
window.onload = () => {
loadSources();
setInterval(loadSources, 10000);
};
</script>
</body>
</html>
This script connects to the API, and takes the names of all the project entries in VMix, detects if there is any entry in the project that has a MultiWindow (The entry name must start with MVW),
It updates in 7 seconds with Vmix, and the input we choose in each selector will be applied to the corresponding MultiWindow (MVW) layer we want.
I hope it helps you, and if someone can modify it to give it more functions, or debug it, welcome.