Remove per-source speed, fix device dirty check, and add frontend caching

Speed is now exclusively controlled via sync clocks — CSS sources no longer
carry their own speed/cycle_speed fields. Streams default to 1.0× when no
clock is assigned. Also fixes false-positive dirty check on the device
settings modal (array reference comparison) and converts several frontend
modules to use DataCache for consistent API response caching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 22:07:54 +03:00
parent aa1e4a6afc
commit 39e41dfce7
15 changed files with 56 additions and 187 deletions

View File

@@ -247,10 +247,8 @@ function restoreCaptureDuration() {
export async function showTestTemplateModal(templateId) {
try {
const resp = await fetchWithAuth('/capture-templates');
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
const template = (data.templates || []).find(tp => tp.id === templateId);
const templates = await captureTemplatesCache.fetch();
const template = templates.find(tp => tp.id === templateId);
if (!template) {
showToast(t('templates.error.load'), 'error');
@@ -1597,34 +1595,25 @@ export async function editStream(streamId) {
let _streamModalDisplaysEngine = null;
async function populateStreamModalDropdowns() {
const [displaysRes, captureTemplatesRes, streamsRes, ppTemplatesRes] = await Promise.all([
fetch(`${API_BASE}/config/displays`, { headers: getHeaders() }),
fetchWithAuth('/capture-templates'),
fetchWithAuth('/picture-sources'),
fetchWithAuth('/postprocessing-templates'),
const [captureTemplates, streams, ppTemplates] = await Promise.all([
captureTemplatesCache.fetch().catch(() => []),
streamsCache.fetch().catch(() => []),
ppTemplatesCache.fetch().catch(() => []),
displaysCache.fetch().catch(() => []),
]);
// Cache desktop displays (used as default unless engine has own displays)
if (displaysRes.ok) {
const displaysData = await displaysRes.json();
displaysCache.update(displaysData.displays || []);
}
_streamModalDisplaysEngine = null;
const templateSelect = document.getElementById('stream-capture-template');
templateSelect.innerHTML = '';
if (captureTemplatesRes.ok) {
const data = await captureTemplatesRes.json();
(data.templates || []).forEach(tmpl => {
const opt = document.createElement('option');
opt.value = tmpl.id;
opt.dataset.name = tmpl.name;
opt.dataset.engineType = tmpl.engine_type;
opt.dataset.hasOwnDisplays = availableEngines.find(e => e.type === tmpl.engine_type)?.has_own_displays ? '1' : '';
opt.textContent = `${tmpl.name} (${tmpl.engine_type})`;
templateSelect.appendChild(opt);
});
}
captureTemplates.forEach(tmpl => {
const opt = document.createElement('option');
opt.value = tmpl.id;
opt.dataset.name = tmpl.name;
opt.dataset.engineType = tmpl.engine_type;
opt.dataset.hasOwnDisplays = availableEngines.find(e => e.type === tmpl.engine_type)?.has_own_displays ? '1' : '';
opt.textContent = `${tmpl.name} (${tmpl.engine_type})`;
templateSelect.appendChild(opt);
});
// When template changes, refresh displays if engine type switched
templateSelect.addEventListener('change', _onCaptureTemplateChanged);
@@ -1640,32 +1629,25 @@ async function populateStreamModalDropdowns() {
const sourceSelect = document.getElementById('stream-source');
sourceSelect.innerHTML = '';
if (streamsRes.ok) {
const data = await streamsRes.json();
const editingId = document.getElementById('stream-id').value;
(data.streams || []).forEach(s => {
if (s.id === editingId) return;
const opt = document.createElement('option');
opt.value = s.id;
opt.dataset.name = s.name;
opt.textContent = s.name;
sourceSelect.appendChild(opt);
});
}
const editingId = document.getElementById('stream-id').value;
streams.forEach(s => {
if (s.id === editingId) return;
const opt = document.createElement('option');
opt.value = s.id;
opt.dataset.name = s.name;
opt.textContent = s.name;
sourceSelect.appendChild(opt);
});
set_streamModalPPTemplates([]);
set_streamModalPPTemplates(ppTemplates);
const ppSelect = document.getElementById('stream-pp-template');
ppSelect.innerHTML = '';
if (ppTemplatesRes.ok) {
const data = await ppTemplatesRes.json();
set_streamModalPPTemplates(data.templates || []);
_streamModalPPTemplates.forEach(tmpl => {
const opt = document.createElement('option');
opt.value = tmpl.id;
opt.textContent = tmpl.name;
ppSelect.appendChild(opt);
});
}
ppTemplates.forEach(tmpl => {
const opt = document.createElement('option');
opt.value = tmpl.id;
opt.textContent = tmpl.name;
ppSelect.appendChild(opt);
});
_autoGenerateStreamName();
}