build157
This commit is contained in:
2
app.py
2
app.py
@@ -30,7 +30,7 @@ from ui_builder import install_ui_builder
|
||||
from khl_site.khl_data_center import APP as khl_site_app
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
BUILD_VERSION = "2026.09.23.3"
|
||||
BUILD_VERSION = "2026.09.23.4"
|
||||
# compatibility: BUILD_VERSION = "2026.08.26.1"
|
||||
# compatibility: BUILD_VERSION = "2026.08.25.1"
|
||||
# compatibility: BUILD_VERSION = "2026.08.24.15"
|
||||
|
||||
@@ -4260,20 +4260,25 @@ function startCustomTooltips() {
|
||||
}
|
||||
|
||||
function vmixGameCountdownControlCommands(input, selectedName, millisecondsProvider, action = "start") {
|
||||
// BUILD108: the native vMix game clock is seeded only on an explicit time change.
|
||||
// Space must never re-seed the countdown: Start/Resume only starts it and Pause/Stop
|
||||
// only pauses it. This keeps the already-running native vMix countdown continuous
|
||||
// and removes jumps caused by writing the browser value during a pause transition.
|
||||
// BUILD157: Runtime is authoritative at every operator Start/Resume transition.
|
||||
// We seed the CURRENT native vMix countdown position once at that transition and
|
||||
// then let vMix count locally. There is still absolutely no per-second vMix write.
|
||||
// This deliberately removes BUILD108/156's fragile browser-only "seeded" assumption:
|
||||
// vMix may have been restarted/reloaded while the browser still remembered the target.
|
||||
const target = { Input: String(input || "").trim(), SelectedName: String(selectedName || "").trim() };
|
||||
if (!target.Input || !target.SelectedName) return [];
|
||||
if (action === "pause" || action === "stop") {
|
||||
return [{ Function: "PauseCountdown", ...target }];
|
||||
}
|
||||
const milliseconds = typeof millisecondsProvider === "function" ? millisecondsProvider() : millisecondsProvider;
|
||||
const value = vmixCountdownValue(milliseconds);
|
||||
if (action === "set") {
|
||||
const milliseconds = typeof millisecondsProvider === "function" ? millisecondsProvider() : millisecondsProvider;
|
||||
return [{ Function: "ChangeCountdown", ...target, Value: vmixCountdownValue(milliseconds) }];
|
||||
return [{ Function: "ChangeCountdown", ...target, Value: value }];
|
||||
}
|
||||
return [{ Function: "StartCountdown", ...target }];
|
||||
return [
|
||||
{ Function: "ChangeCountdown", ...target, Value: value },
|
||||
{ Function: "StartCountdown", ...target },
|
||||
];
|
||||
}
|
||||
|
||||
function buildShortcutRuntimeContext(sequence = null) {
|
||||
@@ -5528,16 +5533,9 @@ function startCustomTooltips() {
|
||||
step.game_vmix_input, step.game_vmix_selected_name, () => gameTimerState.currentMs, "pause"
|
||||
));
|
||||
} else {
|
||||
// BUILD156: the first Start for this exact Input + SelectedName must also
|
||||
// transfer the current Runtime value. This fixes a fresh vMix title where
|
||||
// StartCountdown arrived but the configured web time never did. After the
|
||||
// target is seeded, normal Resume is StartCountdown-only (BUILD108 rule).
|
||||
if (!isVmixGameCountdownSeeded(step.game_vmix_input, step.game_vmix_selected_name)) {
|
||||
commands.push(...vmixGameCountdownControlCommands(
|
||||
step.game_vmix_input, step.game_vmix_selected_name, () => gameTimerState.currentMs, "set"
|
||||
));
|
||||
markVmixGameCountdownSeeded(step.game_vmix_input, step.game_vmix_selected_name);
|
||||
}
|
||||
// BUILD157: every Start/Resume re-seeds from the current Runtime value once,
|
||||
// then starts the native countdown. vMix performs the second-by-second count
|
||||
// locally; Runtime never mirrors timer_tick values to vMix.
|
||||
commands.push(...vmixGameCountdownControlCommands(
|
||||
step.game_vmix_input, step.game_vmix_selected_name, () => gameTimerState.currentMs, "start"
|
||||
));
|
||||
@@ -5574,9 +5572,9 @@ function startCustomTooltips() {
|
||||
eventKey, input: target.input, selectedName: target.selected_name, overlay: target.overlay, sourceSide, targetSide: side, mode: "countdown", running: eventRunning,
|
||||
});
|
||||
|
||||
// BUILD155: native penalty Countdown behaves exactly like the game clock.
|
||||
// Seed only when this vMix slot is assigned to a new penalty. Space pause/resume
|
||||
// is PauseCountdown/StartCountdown only; there is no time writeback on every Space.
|
||||
// BUILD157: penalty countdowns use the same robust transition model as
|
||||
// the game clock. Every Start/Resume sends current remaining time once and
|
||||
// StartCountdown, then vMix counts locally. Pause remains a native pause.
|
||||
if (pausing || !eventRunning) {
|
||||
commands.push(...vmixGameCountdownControlCommands(
|
||||
target.input, target.selected_name, () => event.remainingMs, "pause"
|
||||
@@ -5587,11 +5585,6 @@ function startCustomTooltips() {
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if (assignmentChanged) {
|
||||
commands.push(...vmixGameCountdownControlCommands(
|
||||
target.input, target.selected_name, () => event.remainingMs, "set"
|
||||
));
|
||||
}
|
||||
commands.push(...vmixGameCountdownControlCommands(
|
||||
target.input, target.selected_name, () => event.remainingMs, "start"
|
||||
));
|
||||
@@ -14746,7 +14739,7 @@ function renderHockeyPenaltyDashboard(node, component, runtime) {
|
||||
<div class="shortcut-finish-action-list">${timerFinishActionRows(step)}</div>
|
||||
|
||||
<div class="shortcut-vmix-inventory-note"><span>${escapeHtml(shortcutInventoryLabel())}</span><small>Для каждого countdown теперь обязательно выбирается конкретный Text / SelectedName. Это исключает отправку времени в первый текстовый элемент по умолчанию.</small></div>
|
||||
<p class="shortcut-step-note">Режим <b>Countdown vMix</b>: при первом Start для выбранного Input + SelectedName текущее веб-время один раз передаётся через <code>ChangeCountdown</code>, затем отправляется <code>StartCountdown</code>. После этого Space не пересылает значение: Resume отправляет только <code>StartCountdown</code>, Pause/Stop — только <code>PauseCountdown</code>. Явная установка/сброс времени также обновляет vMix через <code>ChangeCountdown</code>. Это исключает скачки времени при паузе. <code>StopCountdown</code> в обычном управлении не используется. Команды идут в Agent без ACK. <b>Text mirror</b> оставлен только для совместимости со старыми титрами. Для верхнего счёта используется одна penalty-плашка: при реальном большинстве она показывает ближайшее изменение численного состава; при чистом равном обоюдном удалении сама по себе не появляется.</p>
|
||||
<p class="shortcut-step-note">Режим <b>Countdown vMix</b>: каждый Start/Resume один раз передаёт текущее Runtime-время через <code>ChangeCountdown</code>, затем отправляет <code>StartCountdown</code>. После этого vMix считает локально — посекундной передачи времени нет. Pause/Stop отправляет только <code>PauseCountdown</code>. Явная установка/сброс времени также обновляет vMix через <code>ChangeCountdown</code>. Это не зависит от browser-only флага «таймер уже инициализирован» и устойчиво к перезапуску/перезагрузке vMix-титра. <code>StopCountdown</code> в обычном управлении не используется. Команды идут в Agent без ACK. Для верхнего счёта используется одна penalty-плашка: при реальном большинстве она показывает ближайшее изменение численного состава; при чистом равном обоюдном удалении сама по себе не появляется.</p>
|
||||
</div>`;
|
||||
} else if (step.type === "delay") {
|
||||
body.innerHTML = `<div class="shortcut-step-grid"><label>Задержка, мс<input type="number" min="0" max="10000" step="10" data-step-field="milliseconds" value="${Number(step.milliseconds) || 0}"></label></div>`;
|
||||
|
||||
Reference in New Issue
Block a user