Live-Ergebnisse: Stream-Seite, Anzeige, automatischer Mapwechsel

- Stream-Übersicht unter /stream/<Zuschauercode> (1920x1080, Lobby, Veto, Spielstand)
- Live-Stand, Rundenverlauf und Serienstand auf Match-Seite und Übersicht
- Parser mit echten AMP-Logs geprüft: Game Over mit leerer Map-Gruppe,
  Match_Start vor der Aufwärmphase, veraltete Seitenzuordnung nach der Halbzeit
- Nach Map-Ende automatisch nächste Map bzw. Abschluss der entschiedenen Serie
- LOG_DEBUG zur Fehlersuche, README und .env.example ergänzt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
SPP UserandClaude Opus 5.5 committed 2026-10-25 19:20:10 +01:00
1 parent 5fade769e7
commit 43dc5b837a
20 files changed
+901 -44

No files matched your search

+28
View File
@@ -10,6 +10,32 @@ const RETRY = {
};
const ACTIVE = ['starting', 'ready', 'configuring', 'configured', 'config_failed', 'failed'];
/** Zeigt dem Organisator, ob Live-Daten vom Server ankommen und ob die Map vorbei ist. */
function LiveDataStatus({ state, hasNext }) {
const current = state.series.find((s) => s.number === state.currentMapNumber);
if (!current) return null;
const updatedAt = current.live?.updatedAt;
if (current.status === 'done') {
const auto = state.autoAdvanceSeconds >= 0;
return (
<div className="alert alert-ok">
Map {current.number} ist beendet.{' '}
{auto
? (hasNext ? `Die nächste Map wird nach ${state.autoAdvanceSeconds} s automatisch geladen, sofern die Serie nicht entschieden ist.` : 'Das Match wird automatisch abgeschlossen.')
: (hasNext ? 'Lade jetzt die nächste Map.' : 'Das war die letzte Map – schließ das Match ab.')}
</div>
);
}
return (
<p className="small live-data">
<span className={`status-dot ${updatedAt ? 'tone-ok' : 'tone-muted'}`} />
{updatedAt
? `Live-Daten vom Server empfangen, zuletzt um ${new Date(updatedAt).toLocaleTimeString('de-DE')}.`
: 'Noch keine Live-Daten. Sie kommen, sobald die Aufwärmphase endet und der Server diese Webseite erreicht (LOG_RECEIVER_URL).'}
</p>
);
}
export default function ServerPanel({ state, isOrg, act, busy }) {
const { server } = state;
const label = serverLabel(server);
@@ -63,6 +89,8 @@ export default function ServerPanel({ state, isOrg, act, busy }) {
<p className="small muted">CSTV ist auf diesem Server deaktiviert.</p>
)}
{isOrg && server?.status === 'configured' && <LiveDataStatus state={state} hasNext={hasNext} />}
{isOrg && server && !closed && (
<div className="org-controls">
{canRetry && (