In diesem Beitrag zeige ich, wie ich die Einbindung eines Allsky-Livebilds in WordPress per eigenem Plugin gelöst habe.
Ziel war eine performante Darstellung ohne unnötige Ladezeiten – mit serverseitiger Bildoptimierung, automatischer Aktualisierung des Vorschaubilds und einem Overlay für das Originalbild.
Ergänzend informiert ein integriertes Fallback darüber, wenn die Kamera oder die Verbindung temporär nicht verfügbar ist.
Grundlage für den Einsatz des Plugins sind:
- die Verfügbarkeit von indi-allsky im Web – zum Beispiel unter einer eigenen Subdomain
- eine sauber laufende WordPress-Installation – REST wird nicht benötigt
- optional Polylang für Mehrsprachigkeit
Im Folgenden erkläre ich den Aufbau und die einzelnen Funktionsblöcke des Plugins Schritt für Schritt. Weiter unten findet ihr zudem den Download-Link.
Plugin-Header und Grundkonfiguration
Der Plugin-Header macht das Skript für WordPress als Plugin erkennbar. Direkt danach folgen zentrale Konfigurationswerte, die das Verhalten des Plugins steuern.
/*
Plugin Name: Allsky Live (Final Stable)
Description: Allsky-Livebild mit serverseitig optimiertem Preview,
sicherer AJAX-Auslieferung, Originalbild im Overlay,
Auto-Reload, Offline-Fallback. Kein REST.
Version: 2.3.0
Author: allsky-rodgau.de
*/
if (!defined('ABSPATH')) {
exit;
}
define('ALLSKY_SOURCE_URL', 'https://access.allsky-rodgau.de/indi-allsky/latestimage');
define('ALLSKY_REFRESH_MS', 3000);
define('ALLSKY_PREVIEW_WIDTH', 1400);
define('ALLSKY_JPEG_QUALITY', 80);
Hier wird unter anderem festgelegt:
- die Quelle des Livebilds (
latestimage) - das Aktualisierungsintervall in Millisekunden
- die Zielbreite des Vorschaubilds
- die JPEG-Qualität des Previews
Diese Werte bitte an die eigenen Anforderungen anpassen, bevor das Plugin aktiviert wird.
Polylang-Integration und Übersetzungen
Alle sichtbaren Texte des Plugins werden für Polylang registriert.
Wichtig ist dabei, dass die spätere Ausgabe ID-basiert erfolgt und nicht über den Klartext.
add_action('init', function () {
if (function_exists('pll_register_string')) {
pll_register_string('allsky_updated', 'Zuletzt aktualisiert:', 'Allsky Live');
pll_register_string('allsky_hint', 'Zum Vergrößern klicken', 'Allsky Live');
pll_register_string('allsky_close', 'Schließen', 'Allsky Live');
pll_register_string('allsky_loading', 'Lade Bild …', 'Allsky Live');
pll_register_string('allsky_offline', 'Kamera offline seit', 'Allsky Live');
pll_register_string('allsky_minutes', 'Minuten', 'Allsky Live');
}
});
Ergänzt wird dies durch eine kleine Helper-Funktion, die Polylang kapselt und einen Fallback bereitstellt, falls Polylang nicht aktiv ist.
function allsky_t($key, $fallback) {
if (function_exists('pll__')) {
return pll__($key);
}
return $fallback;
}
Cache-Verzeichnis und Sicherheitslogik
Das Plugin legt ein internes Cache-Verzeichnis für Vorschaubilder an. Dieses Verzeichnis ist nicht direkt per HTTP erreichbar, da automatisch eine .htaccess-Datei erzeugt wird.
function allsky_cache_dir() {
$dir = plugin_dir_path(__FILE__) . 'cache/';
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
$htaccess = $dir . '.htaccess';
if (!file_exists($htaccess)) {
file_put_contents($htaccess, "Deny from all\n");
}
return $dir;
}
So bleiben Vorschaubilder geschützt und werden ausschließlich kontrolliert über PHP ausgeliefert.
AJAX-Endpoint: Ermitteln des aktuellen Bildes
Der wichtigste Server-Block ist der AJAX-Handler, der:
- den Redirect von
latestimageauflöst - prüft, ob sich das Bild geändert hat
- bei Bedarf ein neues Preview erzeugt
- Zeitstempel und Dateinamen zurückliefert
add_action('wp_ajax_allsky_latest', 'allsky_ajax_latest');
add_action('wp_ajax_nopriv_allsky_latest', 'allsky_ajax_latest');
Innerhalb dieser Funktion wird das Originalbild geladen, skaliert und als preview.jpg gespeichert. Gleichzeitig wird der Zeitpunkt des letzten erfolgreichen Updates gesichert, um Offline-Zustände erkennen zu können.
Serverseitige Bildoptimierung (Preview)
Die Bildverkleinerung erfolgt mit GD direkt auf dem Server. Nur wenn das Original breiter als die Zielbreite ist, wird tatsächlich skaliert.
$width = imagesx($src);
$height = imagesy($src);
if ($width > ALLSKY_PREVIEW_WIDTH) {
$ratio = $height / $width;
$new_width = ALLSKY_PREVIEW_WIDTH;
$new_height = (int) ($new_width * $ratio);
$dst = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled(
$dst,
$src,
0, 0, 0, 0,
$new_width,
$new_height,
$width,
$height
);
imagedestroy($src);
$src = $dst;
}
imagejpeg($src, $preview, ALLSKY_JPEG_QUALITY);
imagedestroy($src);
AJAX-Endpoint für das Preview-Bild
Da der Cache-Ordner geschützt ist, wird das Vorschaubild über einen separaten AJAX-Endpoint ausgeliefert.
add_action('wp_ajax_allsky_preview', 'allsky_ajax_preview');
add_action('wp_ajax_nopriv_allsky_preview', 'allsky_ajax_preview');
Shortcode und HTML-Struktur
Der Shortcode
[allsky_live]
erzeugt die komplette HTML-Struktur für Preview, Ladebalken, Hinweistext, Zeitstempel und Overlay.
JavaScript: Auto-Reload ohne Layout-Sprünge
Das JavaScript lädt neue Bilder ausschließlich dann, wenn sich der Dateiname ändert. Während des Bildwechsels wird die Bildhöhe fixiert, um Layout-Sprünge zu vermeiden.
let lastFile = null;
let lastOriginal = null;
async function tick() {
try {
const response = await fetch(
ajaxUrl + '?action=allsky_latest',
{ cache: 'no-store' }
);
const data = await response.json();
if (!data.success) {
handleOffline(data);
return;
}
hideOfflineMessage();
infoElement.textContent =
data.data.dt + ' – ' + data.data.filename;
lastOriginal = data.data.original;
if (data.data.filename === lastFile) {
return;
}
loader.style.display = 'block';
const fixedHeight = image.offsetHeight;
if (fixedHeight) {
image.style.height = fixedHeight + 'px';
}
const preload = new Image();
preload.onload = () => {
image.classList.add('fade');
setTimeout(() => {
image.src =
ajaxUrl +
'?action=allsky_preview&t=' +
Date.now();
image.onload = () => {
image.classList.remove('fade');
image.style.height = '';
loader.style.display = 'none';
lastFile = data.data.filename;
};
}, 120);
};
preload.src =
ajaxUrl +
'?action=allsky_preview&t=' +
Date.now();
} catch (e) {
handleOffline(null);
}
}
tick();
setInterval(tick, ALLSKY_REFRESH_INTERVAL);
Overlay für das Originalbild
Beim Klick auf das Preview wird das Originalbild in voller Auflösung im Overlay angezeigt. Die Darstellung erfolgt vollständig per CSS und JavaScript. Das Overlay lädt das Originalbild nur neu, wenn tatsächlich ein neues Kamera-Bild verfügbar ist (Dateiname-Vergleich). Dadurch wird unnötiger Reload vermieden und das Overlay zeigt zuverlässig das neueste Livebild.
<div id="allsky-overlay" class="allsky-overlay">
<button
type="button"
class="allsky-overlay-close"
aria-label="Schließen">
×
</button>
<img
id="allsky-overlay-img"
alt="Allsky Livebild in Originalauflösung">
</div>
let overlayLastFile = null;
let overlayTimer = null;
document.getElementById('allsky-stage').onclick = async () => {
overlay.classList.add('open');
overlayLastFile = lastFile;
overlayImg.src =
ajaxUrl + '?action=allsky_overlay&t=' + Date.now();
overlayTimer = setInterval(async () => {
const r = await fetch(
ajaxUrl + '?action=allsky_latest',
{ cache: 'no-store' }
);
const j = await r.json();
if (!j.success) {
return;
}
if (j.data.filename !== overlayLastFile) {
overlayLastFile = j.data.filename;
overlayImg.src =
ajaxUrl + '?action=allsky_overlay&t=' + Date.now();
}
}, ALLSKY_REFRESH_MS);
};
overlay.querySelector('button').onclick = () => {
overlay.classList.remove('open');
if (overlayTimer) {
clearInterval(overlayTimer);
overlayTimer = null;
}
};
Offline-Fallback
Ist das Livebild nicht erreichbar, zeigt das Plugin automatisch den Hinweis „Kamera offline seit X Minuten“. Die Zeit basiert auf dem letzten erfolgreichen Update.
function handleOffline(data) {
if (!data || !data.last_ok) {
return;
}
const minutesOffline = Math.floor(
(Date.now() / 1000 - data.last_ok) / 60
);
offlineBox.textContent =
offlineText +
' ' +
minutesOffline +
' ' +
offlineMinutesText;
offlineBox.style.display = 'block';
}
function hideOfflineMessage() {
offlineBox.style.display = 'none';
}
Download
Haftungsausschluss
Der hier beschriebene Code sowie das Plugin werden ohne Gewähr bereitgestellt.
Die Nutzung erfolgt auf eigene Verantwortung. Für Schäden, Datenverluste oder Fehlfunktionen, die direkt oder indirekt aus der Anwendung des Codes entstehen, wird keine Haftung übernommen. Vor dem Einsatz in produktiven Umgebungen wird empfohlen, den Code eigenständig zu prüfen und geeignete Backups anzulegen.
Lizenz
Dieses Plugin wird unter der Creative-Commons-Lizenz Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) veröffentlicht.
Die Lizenz erlaubt es Dritten, das Plugin zu vervielfältigen, weiterzugeben, zu verändern und darauf aufzubauen – unabhängig vom Medium oder Format –, ausschließlich zu nicht-kommerziellen Zwecken.
Voraussetzung ist, dass der ursprüngliche Urheber genannt wird. Abgeleitete oder veränderte Fassungen müssen unter derselben Lizenz (CC BY-NC-SA 4.0) weitergegeben werden.