logo

Live Production Software Forums


Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Options
Go to last post Go to first unread
Pothala  
#1 Posted : Thursday, February 27, 2025 5:52:22 AM(UTC)
Pothala

Rank: Newbie

Groups: Registered
Joined: 11/23/2019(UTC)
Posts: 9
Spain
Location: Jerez de la Frontera

Thanks: 1 times
Was thanked: 3 time(s) in 3 post(s)
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.
thanks 1 user thanked Pothala for this useful post.
nikosman88 on 2/28/2025(UTC)
nikosman88  
#2 Posted : Friday, February 28, 2025 12:33:32 AM(UTC)
nikosman88

Rank: Advanced Member

Groups: Registered
Joined: 12/24/2021(UTC)
Posts: 614
Greece
Location: athens

Thanks: 151 times
Was thanked: 82 time(s) in 78 post(s)
Hi. It work ok. A change would be not to be "locked" in MVW input but select your input and send to it whatever you need
I have made an app like this in c# by using also AI but also yours is good
Pothala  
#3 Posted : Friday, February 28, 2025 12:46:47 AM(UTC)
Pothala

Rank: Newbie

Groups: Registered
Joined: 11/23/2019(UTC)
Posts: 9
Spain
Location: Jerez de la Frontera

Thanks: 1 times
Was thanked: 3 time(s) in 3 post(s)
I am debugging it and adding features to it, as soon as I have something a little more stable I will share it. What I am working on now is that I have Tally in the MVW titles, which in the new version will be MW, the VMix IP can be changed, in case you want to manage several machines, and that both the MW inputs and the input selectors are more dynamic and detect the active layers with content, they are updated.
thanks 1 user thanked Pothala for this useful post.
nikosman88 on 2/28/2025(UTC)
Pothala  
#4 Posted : Friday, February 28, 2025 12:48:07 AM(UTC)
Pothala

Rank: Newbie

Groups: Registered
Joined: 11/23/2019(UTC)
Posts: 9
Spain
Location: Jerez de la Frontera

Thanks: 1 times
Was thanked: 3 time(s) in 3 post(s)
For now I'm only testing it with Chrome and it works fine for me.
Pothala  
#5 Posted : Friday, February 28, 2025 12:49:52 AM(UTC)
Pothala

Rank: Newbie

Groups: Registered
Joined: 11/23/2019(UTC)
Posts: 9
Spain
Location: Jerez de la Frontera

Thanks: 1 times
Was thanked: 3 time(s) in 3 post(s)
could you share your c# application for testing?
nikosman88  
#6 Posted : Friday, February 28, 2025 1:24:03 AM(UTC)
nikosman88

Rank: Advanced Member

Groups: Registered
Joined: 12/24/2021(UTC)
Posts: 614
Greece
Location: athens

Thanks: 151 times
Was thanked: 82 time(s) in 78 post(s)
Originally Posted by: Pothala Go to Quoted Post
could you share your c# application for testing?


https://forums.vmix.com/...arty-program-for-Vmix-28
Originally Posted by: Pothala Go to Quoted Post
For now I'm only testing it with Chrome and it works fine for me.

it is ok. it was my fault
Pothala  
#7 Posted : Sunday, March 9, 2025 9:44:16 PM(UTC)
Pothala

Rank: Newbie

Groups: Registered
Joined: 11/23/2019(UTC)
Posts: 9
Spain
Location: Jerez de la Frontera

Thanks: 1 times
Was thanked: 3 time(s) in 3 post(s)
This script is improved. It has a mode selector. For a multi-window entry to be displayed in the application it should be called MW*, and have content, only the layers we want to use, those that do not, should be empty (none). Tally for MW Entries that are in preview or program. It discriminates from the list of entries in the selectors, the entries called MW*. It updates every 7 seconds. If you want to try it, you only have to modify your IP in the HTML.

<!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 {
font-family: Arial, sans-serif;
transition: background-color 0.3s, color 0.3s;
}
.container {
margin-top: 50px;
}
.card {
border-radius: 15px;
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.2);
}
.card-header {
text-align: center;
}
.mw-title-box {
padding: 10px;
border-radius: 8px;
margin-bottom: 15px;
text-align: center;
font-weight: bold;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
}
.form-select, .btn {
border: 1px solid #555;
}
.btn-warning {
background-color: #ff9800;
border: none;
}
.dark-mode {
background-color: #121212;
color: #ffffff;
}
.dark-mode .card {
background-color: #1e1e1e;
}
.dark-mode .card-header {
background-color: #333;
color: white;
}
.dark-mode .form-select, .dark-mode .btn {
background-color: #333;
color: white;
}
</style>
</head>
<body>
<div class="container">
<div class="d-flex justify-content-end">
<button class="btn btn-secondary mb-3" onclick="toggleMode()">MODO</button>
</div>
<div class="card">
<div class="card-header">
<h3><i class="fas fa-video"></i> Selector MW v3.4</h3>
</div>
<div class="card-body">
<div id="mw-entries"></div>
<button type="button" class="btn btn-warning w-100 mt-3" onclick="loadSources()">Actualizar Fuentes</button>
</div>
</div>
</div>

<script>
let selectedSources = {};
let vMixAPI = "http://192.168.68.51:8088/API";
let previewIndex = null;
let activeIndex = null;

function toggleMode() {
document.body.classList.toggle("dark-mode");
}

async function loadSources() {
try {
let response = await fetch(vMixAPI);
if (!response.ok) throw new Error(`Error HTTP: ${response.status}`);
let text = await response.text();
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(text, "text/xml");

let inputs = xmlDoc.getElementsByTagName("input");
let mwEntries = {};

previewIndex = xmlDoc.getElementsByTagName("preview")[0].textContent.trim();
activeIndex = xmlDoc.getElementsByTagName("active")[0].textContent.trim();

Array.from(inputs).forEach(input => {
let title = input.getAttribute("title");
let number = input.getAttribute("number");
if (title && title.startsWith("MW")) {
let mwIndex = title.replace("MW", "").trim();
let overlays = input.getElementsByTagName("overlay");
let mwEntry = { layers: [], state: input.getAttribute("state"), number: number };

Array.from(overlays).forEach(overlay => {
let key = overlay.getAttribute("key");
if (key) {
mwEntry.layers.push(key);
}
});

mwEntries[mwIndex] = mwEntry;
}
});

selectedSources = mwEntries;
renderSources(mwEntries, inputs);
} catch (error) {
alert("Error al conectar con vMix. Verifica la conexión.");
}
}

function renderSources(mwEntries, inputs) {
let mwContainer = document.getElementById("mw-entries");
mwContainer.innerHTML = "";

Object.keys(mwEntries).forEach(mwIndex => {
let entry = mwEntries[mwIndex];
let mwDiv = document.createElement("div");
mwDiv.classList.add("mb-4");

let titleBox = document.createElement("div");
titleBox.classList.add("mw-title-box");
titleBox.id = `title-box-${mwIndex}`;
titleBox.innerHTML = `Entrada MW${mwIndex}`;
updateTitleBoxColor(titleBox, entry.number);

mwDiv.appendChild(titleBox);

entry.layers.forEach((key, i) => {
let label = document.createElement("label");
label.classList.add("form-label");
label.innerText = `Ventana ${i + 1}:`;

let select = document.createElement("select");
select.classList.add("form-select");
select.id = `layer-${mwIndex}-${i}`;
select.onchange = () => updateLayer(mwIndex, i + 1);

Array.from(inputs).forEach(input => {
let title = input.getAttribute("title");
if (!title.startsWith("MW")) {
let option = document.createElement("option");
option.value = input.getAttribute("key");
option.textContent = title;
select.appendChild(option);
}
});

select.value = key;
mwDiv.appendChild(label);
mwDiv.appendChild(select);
});

mwContainer.appendChild(mwDiv);
});
}

function updateTitleBoxColor(titleBox, number) {
titleBox.style.backgroundColor = number === previewIndex ? "green" : number === activeIndex ? "red" : "#007bff";
}

async function updateLayer(mwIndex, layerIndex) {
let selectedKey = document.getElementById(`layer-${mwIndex}-${layerIndex - 1}`).value;
if (!selectedKey) return;
await fetch(`${vMixAPI}?Function=SetMultiViewOverlay&Input=MW${mwIndex}&Value=${layerIndex},${selectedKey}`);
}

window.onload = () => {
loadSources();
setInterval(loadSources, 7000);
};
</script>
</body>
</html>


Users browsing this topic
Guest
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.