diff --git a/.gitignore b/.gitignore index e934adf..cde8d4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ cache/ +__pycache__/ +*.pyc +lara-chess/ diff --git a/README.md b/README.md index b5cb515..6501f9a 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,79 @@ Live-Überwachung von Lara Kiesewetters Partien bei der **ODJM (Offene Deutsche | Proxy-Server | Python http.server (ohne externe Abhängigkeiten) | | PGN-Quelle | deutsche-schachjugend.de (ODJM 2026) | +## Deployment + +Die Anwendung kann als eigenständiger Server betrieben werden. + +### Produktivbetrieb (systemd – Linux) + +```bash +# Service-Datei erstellen +sudo tee /etc/systemd/system/lara-chess.service <= 0) { + // Benutzer hat navigiert – zeige Brett an seinem ausgewählten Zug + chess = new Chess(); + for (let i = 0; i <= currentMoveIndex && i < nonResultMoves.length; i++) { + try { + chess.move(nonResultMoves[i].san); + } catch (e) { + break; + } + } + } if (board) { board.position(chess.fen(), true); @@ -122,6 +139,7 @@ function goToMove(index) { if (index < -1) index = -1; if (index >= nonResultMoves.length) index = nonResultMoves.length - 1; + userScrolledMoves = index < nonResultMoves.length - 1; currentMoveIndex = index; chess = new Chess(); @@ -135,6 +153,7 @@ function goToMove(index) { board.position(chess.fen(), true); highlightActivePlayer(); + updateClocks(index); document.querySelectorAll('#moves-list .move').forEach(el => el.classList.remove('current')); if (index >= 0) { @@ -153,26 +172,24 @@ function updatePlayerInfo() { const laraPlayer = laraIsWhite ? currentGame.white : currentGame.black; const laraElo = laraIsWhite ? currentGame.whiteElo : currentGame.blackElo; - const laraClock = laraIsWhite ? currentGame.whiteClock : currentGame.blackClock; const laraEmoji = laraIsWhite ? '⬜' : '⬛'; const oppPlayer = laraIsWhite ? currentGame.black : currentGame.white; const oppElo = laraIsWhite ? currentGame.blackElo : currentGame.whiteElo; - const oppClock = laraIsWhite ? currentGame.blackClock : currentGame.whiteClock; const oppEmoji = laraIsWhite ? '⬛' : '⬜'; // Top-Panel (player-black) = Gegner document.getElementById('black-name').textContent = oppPlayer; document.getElementById('black-elo').textContent = oppElo ? `(ELO: ${oppElo})` : ''; - document.getElementById('black-clock').textContent = formatClock(oppClock); document.querySelector('#player-black .player-avatar').textContent = oppEmoji; // Bottom-Panel (player-white) = Lara document.getElementById('white-name').textContent = laraPlayer; document.getElementById('white-elo').textContent = laraElo ? `(ELO: ${laraElo})` : ''; - document.getElementById('white-clock').textContent = formatClock(laraClock); document.querySelector('#player-white .player-avatar').textContent = laraEmoji; + updateClocks(currentMoveIndex); + // Round info const roundInfo = `Runde ${currentGame.round} – ${currentGame.event || 'Turnier'}`; document.getElementById('round-info').textContent = roundInfo; @@ -186,6 +203,34 @@ function updatePlayerInfo() { } } +/** + * Aktualisiert die Uhrenanzeige basierend auf dem aktuellen Zugindex + */ +function updateClocks(moveIndex) { + if (!currentGame) return; + + const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter'); + + let whiteClock, blackClock; + + if (moveIndex < 0) { + whiteClock = currentGame.whiteClock; + blackClock = currentGame.blackClock; + } else { + const nonResultMoves = currentGame.moves.filter(m => !m.isResult); + if (moveIndex < nonResultMoves.length) { + whiteClock = nonResultMoves[moveIndex].whiteClock; + blackClock = nonResultMoves[moveIndex].blackClock; + } + } + + const laraClock = laraIsWhite ? whiteClock : blackClock; + const oppClock = laraIsWhite ? blackClock : whiteClock; + + document.getElementById('black-clock').textContent = formatClock(oppClock); + document.getElementById('white-clock').textContent = formatClock(laraClock); +} + /** * Highlight den Spieler, der gerade am Zug ist */ @@ -267,8 +312,10 @@ function updateMovesList() { if (curMove) curMove.classList.add('current'); } - // Scroll to bottom - movesList.scrollTop = movesList.scrollHeight; + // Scroll to bottom nur wenn Benutzer nicht manuell navigiert hat + if (!userScrolledMoves) { + movesList.scrollTop = movesList.scrollHeight; + } } /** @@ -308,6 +355,7 @@ function updateAllGamesList() { entry.addEventListener('click', () => { userSelectedGame = true; + userScrolledMoves = false; currentGame = game; updateBoard(); updatePlayerInfo(); @@ -354,7 +402,7 @@ function startAutoRefresh() { if (countdown <= 0) { countdown = REFRESH_INTERVAL / 1000; - loadPGN(); + loadPGN(false); } }, 1000); } diff --git a/server.py b/server.py index 7aa0ccb..0f99bec 100644 --- a/server.py +++ b/server.py @@ -14,11 +14,11 @@ import time import json from datetime import datetime -PGN_URL = "https://www.deutsche-schachjugend.de/2026/odjm-d/partien/gesamt-utf8.pgn" -PORT = 8111 +PGN_URL = os.environ.get("PGN_URL", "https://www.deutsche-schachjugend.de/2026/odjm-d/partien/gesamt-utf8.pgn") +PORT = int(os.environ.get("PORT", 8111)) CACHE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cache") CACHE_FILE = os.path.join(CACHE_DIR, "gesamt-utf8.pgn") -CACHE_TTL = 30 # Sekunden +CACHE_TTL = int(os.environ.get("CACHE_TTL", 30)) # Sekunden os.makedirs(CACHE_DIR, exist_ok=True)