AstraOS-Panel/Outdated Website/templates/wallpaper.html
2025-12-06 21:12:08 +01:00

377 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SCP:SL Server Status</title>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
<style>
:root {
--primary-bg-color: rgba(0, 0, 0, 0.3);
--text-color: #e0e0e0;
--highlight-color: #00ff00;
--error-color: #ff4444;
--border-color: rgba(255, 255, 255, 0.08);
--shadow-color: rgba(0, 0, 0, 0.6);
--angle: 90deg;
--warning-color: #ffcc00;
}
body {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
background: linear-gradient(180deg, #07131c, #305472);
font-family: 'Roboto', sans-serif;
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.server-info-card {
position: relative;
z-index: 10;
text-align: center;
background-color: var(--primary-bg-color);
padding: 40px 70px;
border-radius: 16px;
box-shadow: 0 10px 32px var(--shadow-color);
max-width: 98%;
width: 1000px;
border: 1px solid var(--border-color);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
transform: translateY(0);
opacity: 1;
transition: transform 0.5s ease-out, opacity 0.5s ease-out;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.server-info-card.hidden {
transform: translateY(20px);
opacity: 0;
pointer-events: none;
}
#server-name {
font-size: 3.125em;
margin-bottom: 18px;
font-weight: 700;
line-height: 1.2;
letter-spacing: 0.5px;
text-transform: none;
animation: fadeIn 1.5s ease-in-out forwards;
text-shadow: 1px 1px 3px var(--shadow-color);
word-break: break-word;
}
#server-name span {
display: inline-block;
}
#player-count {
font-size: 2.75em;
margin-top: 18px;
font-weight: 700;
color: var(--highlight-color);
transition: color 0.3s ease-in-out;
text-shadow: 1px 1px 3px var(--shadow-color);
margin-bottom: 25px;
}
.loading-message {
font-size: 1.75em;
color: var(--text-color);
animation: pulse 1.5s infinite alternate;
}
.error-message {
font-size: 1.75em;
color: var(--error-color);
font-weight: 700;
}
.warning-message {
font-size: 1.75em;
color: var(--warning-color);
font-weight: 700;
}
.rain {
position: absolute;
width: 100vw;
height: 100vh;
z-index: 0;
top: 0;
left: 0;
pointer-events: none;
overflow: hidden;
}
.drop {
border: 0.25vmin solid transparent;
border-bottom-color: #abc2e9;
position: absolute;
top: -5vmin;
width: 0;
height: 0;
animation: fall var(--drop-duration) var(--drop-delay) linear infinite;
left: var(--drop-left);
border-left-width: var(--drop-width);
opacity: var(--drop-opacity);
transform: rotate(var(--angle));
transform-origin: 50% 100%;
}
@keyframes fall {
from {
transform: translateY(0) rotate(var(--angle));
}
to {
transform: translateY(calc(100vh + 10vmin)) rotate(var(--angle));
}
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
from { opacity: 0.7; }
to { opacity: 1; }
}
.graph-image-container {
width: 100%;
max-width: 900px;
height: auto;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 22px;
padding: 32px;
box-sizing: border-box;
margin-top: 36px;
display: flex;
justify-content: center;
align-items: center;
}
.graph-image-container img {
max-width: 100%;
height: auto;
display: block;
border-radius: 14px;
}
@media (max-width: 1100px) {
.server-info-card {
width: 98%;
padding: 25px 10px;
}
.graph-image-container {
max-width: 98vw;
padding: 10px;
}
}
@media (max-width: 768px) {
.server-info-card {
padding: 18px 2vw;
width: 99vw;
}
#server-name {
font-size: 2.25em;
}
#player-count {
font-size: 2.0em;
}
.loading-message, .error-message, .warning-message {
font-size: 1.25em;
}
.graph-image-container {
max-width: 100vw;
height: auto;
padding: 4vw;
}
}
</style>
</head>
<body>
<div class="rain"></div>
<div class="server-info-card">
<div id="server-name" class="loading-message">Lade Servernamen...</div>
<div id="player-count" class="loading-message">Lade Spieleranzahl...</div>
<div id="global-error-message" class="error-message" style="display:none; margin-top: 10px;"></div>
<div class="graph-image-container">
<img id="playerHistoryGraph" src="/player_history_graph_img" alt="Spielerstatistik der letzten 24h">
</div>
</div>
<script>
const numDrops = 500;
const rainContainer = document.querySelector('.rain');
const defaultAngle = 90;
let initialLoadDone = false;
for (let i = 0; i < numDrops; i++) {
const drop = document.createElement('div');
drop.classList.add('drop');
drop.style.setProperty('--drop-opacity', (Math.random() * 0.7) + 0.1);
drop.style.setProperty('--drop-left', (Math.random() * 100) + 'vw');
drop.style.setProperty('--drop-width', (Math.random() * 0.4) + 0.05 + 'vmin');
drop.style.setProperty('--drop-duration', (Math.random() * 1.5) + 1.5 + 's');
drop.style.setProperty('--drop-delay', (Math.random() * -3) + 's');
drop.style.setProperty('--angle', `${defaultAngle}deg`);
rainContainer.appendChild(drop);
}
function extractShortServerName(rawInfo) {
const nameMatch = rawInfo.match(/(<span style="color:#[a-fA-F0-9]{6}">\[G<\/span><span style="color:#[a-fA-F0-9]{6}">E<\/span><span style="color:#[a-fA-F0-9]{6}">R\]<\/span>)?\s*<b>(.*?)<\/b>(\s*<span style="color:#[a-fA-F0-9]{6}">\[#\d+\]<\/span>)?/);
if (nameMatch) {
let finalName = '';
if (nameMatch[1]) finalName += nameMatch[1] + ' ';
if (nameMatch[2]) finalName += nameMatch[2];
if (nameMatch[3]) finalName += nameMatch[3];
return finalName.split('<br')[0].trim();
}
return rawInfo.split('<br')[0].replace(/<[^>]*>/g, '').trim();
}
function updatePlayerHistoryGraph() {
const graphImage = document.getElementById('playerHistoryGraph');
graphImage.src = `/player_history_graph_img?t=${new Date().getTime()}`;
}
async function fetchServerData(isInitialCall = false) {
const apiUrl = '/api/scpsl_proxy';
const serverNameElement = document.getElementById('server-name');
const playerCountElement = document.getElementById('player-count');
const cardElement = document.querySelector('.server-info-card');
const globalErrorMessageElement = document.getElementById('global-error-message');
if (isInitialCall || !initialLoadDone || serverNameElement.classList.contains('error-message') || globalErrorMessageElement.style.display === 'block') {
serverNameElement.className = 'loading-message';
serverNameElement.innerHTML = 'Lade Servernamen...';
playerCountElement.className = 'loading-message';
playerCountElement.textContent = 'Lade Spieleranzahl...';
globalErrorMessageElement.style.display = 'none';
cardElement.classList.remove('hidden');
} else {
if (!playerCountElement.classList.contains('error-message')) {
playerCountElement.classList.add('loading-message');
}
}
const timeoutPromise = new Promise((resolve, reject) => {
const id = setTimeout(() => {
clearTimeout(id);
reject(new Error('Server response timed out (mehr als 5 Sekunden).'));
}, 5000);
});
try {
const response = await Promise.race([
fetch(apiUrl),
timeoutPromise
]);
if (!response.ok) {
let errorDetails = 'Unbekannter Fehler';
try {
const errorData = await response.json();
errorDetails = errorData.error || errorDetails;
} catch (e) {
errorDetails = response.statusText || errorDetails;
}
throw new Error(`Serverfehler: ${response.status} - ${errorDetails}`);
}
const data = await response.json();
let rawServerInfo = data.info || "Unbekannter Server";
const currentServerNameHTML = serverNameElement.innerHTML;
const newServerNameHTML = extractShortServerName(rawServerInfo);
if (isInitialCall || !initialLoadDone || serverNameElement.classList.contains('error-message') || currentServerNameHTML !== newServerNameHTML) {
serverNameElement.innerHTML = newServerNameHTML;
}
if (data.online) {
playerCountElement.textContent = `Spieler: ${data.players}`;
playerCountElement.style.color = 'var(--highlight-color)';
serverNameElement.classList.remove('loading-message', 'error-message', 'warning-message');
playerCountElement.classList.remove('loading-message', 'error-message', 'warning-message');
globalErrorMessageElement.style.display = 'none';
cardElement.classList.remove('hidden');
} else {
if (!serverNameElement.innerHTML.includes('[OFFLINE]')) {
serverNameElement.innerHTML = newServerNameHTML + '<br><span style="font-size:0.6em; color:var(--error-color);">[OFFLINE]</span>';
}
playerCountElement.textContent = 'Server ist offline.';
playerCountElement.style.color = 'var(--error-color)';
serverNameElement.classList.remove('loading-message', 'warning-message');
serverNameElement.classList.add('error-message');
playerCountElement.classList.remove('loading-message', 'warning-message');
playerCountElement.classList.add('error-message');
globalErrorMessageElement.style.display = 'none';
cardElement.classList.remove('hidden');
}
playerCountElement.classList.remove('loading-message');
initialLoadDone = true;
} catch (error) {
console.error('Fehler beim Abrufen der Serverdaten:', error);
let errorMessage = 'Server konnte nicht erreicht werden.';
let errorSubtext = '<i>(Allgemeiner Fehler)</i>';
let errorClass = 'error-message';
if (error.message.includes('timed out')) {
errorMessage = 'Server-Antwort Timeout.';
errorSubtext = '<i>(Ladeversuch läuft im Hintergrund)</i>';
errorClass = 'warning-message';
} else if (error.message.includes('Serverfehler:')) {
errorMessage = 'Serverfehler.';
errorSubtext = `<i>(${error.message.split('Serverfehler: ')[1]})</i>`;
errorClass = 'error-message';
}
serverNameElement.className = errorClass;
serverNameElement.innerHTML = errorMessage + '<br><small>' + errorSubtext + '</small>';
playerCountElement.className = errorClass;
playerCountElement.textContent = 'Daten nicht verfügbar.';
globalErrorMessageElement.textContent = 'Aktualisierung fehlgeschlagen. Bitte versuche es später erneut.';
globalErrorMessageElement.style.display = 'block';
if (errorClass === 'error-message') {
cardElement.classList.add('hidden');
} else {
cardElement.classList.remove('hidden');
}
initialLoadDone = false;
}
}
document.addEventListener('DOMContentLoaded', () => {
fetchServerData(true);
updatePlayerHistoryGraph();
setInterval(fetchServerData, 5000);
setInterval(updatePlayerHistoryGraph, 60000);
});
</script>
</body>
</html>