синхронизация событий со всеми браузерами, тест 1
This commit is contained in:
12
app.py
12
app.py
@@ -1222,6 +1222,18 @@ def api_clear_events(session_token: str):
|
|||||||
|
|
||||||
match_id = session_row[1]
|
match_id = session_row[1]
|
||||||
clear_events(match_id)
|
clear_events(match_id)
|
||||||
|
|
||||||
|
# Если события очищены в одном браузере, сбрасываем общий серверный таймер тоже.
|
||||||
|
# Иначе другой браузер с запущенным таймером может снова подтянуть старое время.
|
||||||
|
MATCH_CLOCK_STATES[match_id] = {
|
||||||
|
"currentPeriod": None,
|
||||||
|
"matchClockSeconds": 0,
|
||||||
|
"timerRunning": False,
|
||||||
|
"pausedAccumulatedSeconds": 0,
|
||||||
|
"serverSavedAt": time.time(),
|
||||||
|
"sourceClientId": "events_clear",
|
||||||
|
}
|
||||||
|
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ let lastClockServerSyncAt = 0;
|
|||||||
let clockServerSyncInFlight = false;
|
let clockServerSyncInFlight = false;
|
||||||
let applyingRemoteClockState = false;
|
let applyingRemoteClockState = false;
|
||||||
let clockEditorIsOpen = false;
|
let clockEditorIsOpen = false;
|
||||||
|
let eventsServerSyncInFlight = false;
|
||||||
|
let applyingRemoteEventsState = false;
|
||||||
|
let lastEventsSnapshot = "";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const formationState = {
|
const formationState = {
|
||||||
@@ -326,7 +330,6 @@ function saveMatchState() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));
|
||||||
syncClockStateToServer(false);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Не удалось сохранить состояние матча", e);
|
console.warn("Не удалось сохранить состояние матча", e);
|
||||||
}
|
}
|
||||||
@@ -860,6 +863,7 @@ function setMatchPeriod(period) {
|
|||||||
startTimer();
|
startTimer();
|
||||||
saveMatchState();
|
saveMatchState();
|
||||||
syncClockStateToServer(true);
|
syncClockStateToServer(true);
|
||||||
|
syncClockStateToServer(true);
|
||||||
updatePeriodButtons();
|
updatePeriodButtons();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1107,7 +1111,7 @@ function stepExtraTime(step) {
|
|||||||
input.value = value;
|
input.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearMatchEvents() {
|
async function clearMatchEvents() {
|
||||||
if (timerId) {
|
if (timerId) {
|
||||||
clearInterval(timerId);
|
clearInterval(timerId);
|
||||||
timerId = null;
|
timerId = null;
|
||||||
@@ -1136,8 +1140,8 @@ function clearMatchEvents() {
|
|||||||
renderEvents();
|
renderEvents();
|
||||||
|
|
||||||
saveMatchState();
|
saveMatchState();
|
||||||
syncClockStateToServer(true);
|
await clearEventsOnServer();
|
||||||
clearEventsOnServer();
|
await syncClockStateToServer(true);
|
||||||
updateCoachCardButtons();
|
updateCoachCardButtons();
|
||||||
updatePeriodButtons();
|
updatePeriodButtons();
|
||||||
triggerVmixTimerAction("clear_match");
|
triggerVmixTimerAction("clear_match");
|
||||||
@@ -1680,18 +1684,39 @@ function restoreEventsClickOrder(serverEvents, persistedEvents) {
|
|||||||
|
|
||||||
|
|
||||||
async function loadEventsFromServer() {
|
async function loadEventsFromServer() {
|
||||||
if (!SESSION_TOKEN) return;
|
if (!SESSION_TOKEN || applyingRemoteEventsState || clockEditorIsOpen) return;
|
||||||
|
if (eventsServerSyncInFlight) return;
|
||||||
|
|
||||||
|
eventsServerSyncInFlight = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const persisted = getPersistedState();
|
const persisted = getPersistedState();
|
||||||
const persistedEvents = Array.isArray(persisted?.matchEvents) ? persisted.matchEvents : [];
|
const persistedEvents = Array.isArray(persisted?.matchEvents) ? persisted.matchEvents : [];
|
||||||
|
|
||||||
const res = await fetch(`/admin/session/${SESSION_TOKEN}/events`);
|
const res = await fetch(`/admin/session/${SESSION_TOKEN}/events`, { cache: "no-store" });
|
||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (!Array.isArray(data)) return;
|
if (!Array.isArray(data)) return;
|
||||||
|
|
||||||
|
const snapshot = JSON.stringify(data.map((event) => ({
|
||||||
|
id: event.id ?? null,
|
||||||
|
side: event.side ?? "",
|
||||||
|
type: event.type ?? "",
|
||||||
|
player_name: event.player_name ?? "",
|
||||||
|
minute: event.minute ?? null,
|
||||||
|
seconds: event.seconds ?? 0,
|
||||||
|
meta: event.meta ?? null,
|
||||||
|
player_id: event.player_id ?? null,
|
||||||
|
player_out_id: event.player_out_id ?? null,
|
||||||
|
player_in_id: event.player_in_id ?? null
|
||||||
|
})));
|
||||||
|
|
||||||
|
if (snapshot === lastEventsSnapshot) return;
|
||||||
|
lastEventsSnapshot = snapshot;
|
||||||
|
|
||||||
|
applyingRemoteEventsState = true;
|
||||||
|
|
||||||
matchEvents = data.map((event) => {
|
matchEvents = data.map((event) => {
|
||||||
const fallback = persistedEvents.find((p) =>
|
const fallback = persistedEvents.find((p) =>
|
||||||
String(p.type || "") === String(event.type || "") &&
|
String(p.type || "") === String(event.type || "") &&
|
||||||
@@ -1709,8 +1734,6 @@ async function loadEventsFromServer() {
|
|||||||
minute: typeof event.minute === "string" && event.minute.includes("'")
|
minute: typeof event.minute === "string" && event.minute.includes("'")
|
||||||
? event.minute
|
? event.minute
|
||||||
: (fallback?.minute || getEventDisplayMinuteBySeconds(event.seconds || 0, event.type)),
|
: (fallback?.minute || getEventDisplayMinuteBySeconds(event.seconds || 0, event.type)),
|
||||||
|
|
||||||
|
|
||||||
player_id: event.player_id ?? fallback?.player_id ?? null,
|
player_id: event.player_id ?? fallback?.player_id ?? null,
|
||||||
player_out_id: event.player_out_id ?? fallback?.player_out_id ?? null,
|
player_out_id: event.player_out_id ?? fallback?.player_out_id ?? null,
|
||||||
player_in_id: event.player_in_id ?? fallback?.player_in_id ?? null
|
player_in_id: event.player_in_id ?? fallback?.player_in_id ?? null
|
||||||
@@ -1719,8 +1742,6 @@ async function loadEventsFromServer() {
|
|||||||
|
|
||||||
matchEvents = restoreEventsClickOrder(matchEvents, persistedEvents);
|
matchEvents = restoreEventsClickOrder(matchEvents, persistedEvents);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
scoreState = { home: 0, away: 0 };
|
scoreState = { home: 0, away: 0 };
|
||||||
matchEvents.forEach((event) => {
|
matchEvents.forEach((event) => {
|
||||||
if (["goal", "penalty", "own_goal"].includes(event.type)) {
|
if (["goal", "penalty", "own_goal"].includes(event.type)) {
|
||||||
@@ -1728,6 +1749,12 @@ async function loadEventsFromServer() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!matchEvents.length) {
|
||||||
|
activeSubstitution = null;
|
||||||
|
clearBenchSelection();
|
||||||
|
restoreOriginalLineupRows();
|
||||||
|
}
|
||||||
|
|
||||||
updateScoreUI();
|
updateScoreUI();
|
||||||
renderEvents();
|
renderEvents();
|
||||||
rebuildLineupsFromEvents();
|
rebuildLineupsFromEvents();
|
||||||
@@ -1736,9 +1763,17 @@ async function loadEventsFromServer() {
|
|||||||
updatePeriodButtons();
|
updatePeriodButtons();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Не удалось загрузить события из БД", e);
|
console.warn("Не удалось загрузить события из БД", e);
|
||||||
|
} finally {
|
||||||
|
applyingRemoteEventsState = false;
|
||||||
|
eventsServerSyncInFlight = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initEventsStateSync() {
|
||||||
|
loadEventsFromServer();
|
||||||
|
setInterval(loadEventsFromServer, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
async function sendDirectVmixCommands(commands) {
|
async function sendDirectVmixCommands(commands) {
|
||||||
const items = Array.isArray(commands)
|
const items = Array.isArray(commands)
|
||||||
? commands.map(x => String(x || "").trim()).filter(Boolean)
|
? commands.map(x => String(x || "").trim()).filter(Boolean)
|
||||||
@@ -2530,7 +2565,7 @@ if (!restored) {
|
|||||||
saveMatchState();
|
saveMatchState();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadEventsFromServer();
|
initEventsStateSync();
|
||||||
initClockStateSync();
|
initClockStateSync();
|
||||||
window.addEventListener("beforeunload", saveMatchState);
|
window.addEventListener("beforeunload", saveMatchState);
|
||||||
window.addEventListener("pagehide", saveMatchState);
|
window.addEventListener("pagehide", saveMatchState);
|
||||||
|
|||||||
Reference in New Issue
Block a user