'; ai wieder einblende das unten rausnehmendas rein
$output .= '
';
$output .= '🤖 AI-Erkennung: ';
foreach ($this->aiCategories as $cat => $info) {
$output .= '' . $info['icon'] . ' ';
}
$output .= '
';
// Kalender-Grid
$output .= '
';
// Wochentage Header
$weekdays = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'];
foreach ($weekdays as $day) {
$output .= '
' . $day . '
';
}
// Tage des Monats
$firstDay = mktime(0, 0, 0, $currentMonth, 1, $currentYear);
$daysInMonth = date('t', $firstDay);
$dayOfWeek = date('N', $firstDay) - 1;
// Leere Zellen vor dem ersten Tag
for ($i = 0; $i < $dayOfWeek; $i++) {
$output .= '
';
}
// Tage mit Videos/AI-Events markieren
for ($day = 1; $day <= $daysInMonth; $day++) {
$hasVideos = $this->hasVideosForDate($currentYear, $currentMonth, $day);
$hasAiEvents = $this->hasAiEventsForDate($currentYear, $currentMonth, $day);
$aiIcons = $this->getAiIconsForDate($currentYear, $currentMonth, $day);
$isSelected = ($selectedDay == $day);
$isToday = ($currentYear == date('Y') && $currentMonth == date('n') && $day == date('j'));
$classes = 'calendar-day';
if ($hasVideos) $classes .= ' has-video';
if ($hasAiEvents) $classes .= ' has-ai-events';
if ($isSelected) $classes .= ' selected';
if ($isToday) $classes .= ' today';
$output .= '
';
$output .= '
' . $day . ' ';
// Video-Indikator
if ($hasVideos) {
$output .= '
📹 ';
}
// AI-Event-Icons (max 3 anzeigen)
if (!empty($aiIcons)) {
$output .= '
';
$displayIcons = array_slice($aiIcons, 0, 3);
foreach ($displayIcons as $icon) {
$output .= '' . $icon . ' ';
}
if (count($aiIcons) > 3) {
$output .= '+' . (count($aiIcons) - 3) . ' ';
}
$output .= '
';
}
$output .= '
';
}
$output .= '
'; // calendar-grid
// Video-Liste + AI-Events für ausgewählten Tag
if ($selectedDay) {
$videos = $this->getVideosForDate($currentYear, $currentMonth, $selectedDay);
$aiEvents = $this->getAiEventsForDate($currentYear, $currentMonth, $selectedDay);
$output .= '
';
$output .= '
📅 ' . sprintf('%02d.%02d.%04d', $selectedDay, $currentMonth, $currentYear) . ' ';
// === TAGESVIDEOS ===
if (!empty($videos)) {
$output .= '
';
$output .= '
📹 Tagesvideos ';
$output .= '
';
foreach ($videos as $video) {
$sizeInMb = round($video['filesize'] / (1024 * 1024), 2);
$token = hash_hmac('sha256', $video['path'], session_id());
$videoUrl = '?download_specific_video=' . urlencode($video['path']) . '&token=' . $token;
$videoWebUrl = $this->videoPathToUrl($video['path']);
$output .= '';
$output .= '🕐 ' . $video['time'] . ' Uhr ';
$output .= '' . $sizeInMb . ' MB ';
$output .= '';
$output .= ' ';
}
$output .= ' ';
$output .= '
';
}
// === AI-EREIGNISSE ===
if (!empty($aiEvents) && (!$this->settingsManager || $this->settingsManager->isAIEventsEnabled())) {
$output .= '
';
$output .= '
🤖 AI-erkannte Ereignisse ';
$output .= '
';
foreach ($aiEvents as $category => $event) {
$output .= '
';
$output .= '';
// Videos für dieses Event
$output .= '
';
$output .= '
'; // ai-event-card
}
$output .= '
'; // ai-events-grid
$output .= '
'; // ai-events-section
}
// Keine Inhalte
if (empty($videos) && empty($aiEvents)) {
$output .= '
';
$output .= '
📭 Keine Videos oder AI-Ereignisse für diesen Tag verfügbar.
';
$output .= '
';
}
$output .= '
'; // day-details
}
$output .= '
'; // visual-calendar-container
return $output;
}
/**
* Handler für AI-Video Downloads
*/
public function handleAiVideoDownload() {
if (isset($_GET['download_ai_video']) && isset($_GET['token'])) {
$videoPath = $_GET['download_ai_video'];
$token = $_GET['token'];
// Token-Validierung
$expectedToken = hash_hmac('sha256', $videoPath, session_id());
if (!hash_equals($expectedToken, $token)) {
echo "Ungültiger Token. Zugriff verweigert.";
exit;
}
// Sicherheitsüberprüfung
$aiDir = realpath($this->aiDir);
$requestedPath = realpath($videoPath);
if ($requestedPath && strpos($requestedPath, $aiDir) === 0 && file_exists($requestedPath)) {
$extension = pathinfo($requestedPath, PATHINFO_EXTENSION);
if (strtolower($extension) !== 'mp4') {
echo "Nur MP4-Dateien können heruntergeladen werden.";
exit;
}
header('Content-Description: File Transfer');
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="'.basename($requestedPath).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($requestedPath));
readfile($requestedPath);
exit;
} else {
echo "Datei nicht gefunden oder ungültiger Dateipfad.";
exit;
}
}
}
}
class GuestbookManager {
private $entries = [];
private $dbFile = 'guestbook.json';
public function __construct() {
if (file_exists($this->dbFile)) {
$this->entries = json_decode(file_get_contents($this->dbFile), true);
}
}
public function handleFormSubmission() {
if (isset($_POST['guestbook'], $_POST['guest-name'], $_POST['guest-message'])) {
$this->addEntry($_POST['guest-name'], $_POST['guest-message']);
$this->saveEntries();
}
}
private function addEntry($name, $message) {
$this->entries[] = [
'name' => $name,
'message' => $message,
'date' => date('Y-m-d H:i:s')
];
}
public function deleteEntry($index) {
if (isset($this->entries[$index])) {
unset($this->entries[$index]);
$this->entries = array_values($this->entries); // Re-indizieren des Arrays
$this->saveEntries();
return true;
}
return false;
}
private function saveEntries() {
file_put_contents($this->dbFile, json_encode($this->entries));
}
public function displayForm() {
return '
';
foreach ($this->entries as $index => $entry) {
$output .= "
{$entry['name']}
{$entry['message']}
{$entry['date']}";
if ($isAdmin) {
$output .= "
";
}
}
$output .= '
';
return $output;
}
}
class ContactManager {
private $adminEmail = 'metacube@gmail.com';
private $feedbackFile = 'feedbacks.json';
private $gmailUser = 'metacube@gmail.com';
private $gmailAppPassword = 'qggk hsxz fdkq jgxa';
public function displayForm() {
return '
';
}
public function handleSubmission($name, $email, $message) {
if (empty($name) || empty($email) || empty($message)) {
return ['success' => false, 'message' => 'Alle Felder sind erforderlich'];
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return ['success' => false, 'message' => 'Ungültige E-Mail-Adresse'];
}
if (strlen($message) < 10) {
return ['success' => false, 'message' => 'Nachricht zu kurz (mindestens 10 Zeichen)'];
}
$name = htmlspecialchars(trim($name), ENT_QUOTES, 'UTF-8');
$email = filter_var(trim($email), FILTER_SANITIZE_EMAIL);
$message = htmlspecialchars(trim($message), ENT_QUOTES, 'UTF-8');
$feedback = [
'name' => $name,
'email' => $email,
'message' => $message,
'date' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
];
$feedbacks = file_exists($this->feedbackFile)
? json_decode(file_get_contents($this->feedbackFile), true)
: [];
if (!is_array($feedbacks)) $feedbacks = [];
$feedbacks[] = $feedback;
file_put_contents($this->feedbackFile, json_encode($feedbacks, JSON_PRETTY_PRINT));
$mailSent = $this->sendEmailViaGmail($name, $email, $message, $feedback['date'], $feedback['ip']);
if ($mailSent) {
return ['success' => true, 'message' => 'Vielen Dank! Ihre Nachricht wurde gesendet.'];
} else {
error_log("Mail-Fehler: Nachricht von {$email} konnte nicht gesendet werden");
return ['success' => false, 'message' => 'Nachricht wurde gespeichert, aber E-Mail konnte nicht gesendet werden.'];
}
}
private function sendEmailViaGmail($name, $email, $message, $date, $ip) {
$mail = new PHPMailer(true);
try {
$mail->SMTPDebug = 2;
$mail->Debugoutput = function($str, $level) {
error_log("PHPMailer Debug: $str");
};
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = $this->gmailUser;
$mail->Password = $this->gmailAppPassword;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom($this->gmailUser, 'Aurora Livecam');
$mail->addAddress($this->adminEmail);
$mail->addReplyTo($email, $name);
$mail->isHTML(true);
$mail->CharSet = 'UTF-8';
$mail->Subject = '🔔 Neue Kontaktanfrage von ' . $name;
$mail->Body = $this->getEmailTemplate($name, $email, $message, $date, $ip);
$mail->send();
return true;
} catch (Exception $e) {
error_log("PHPMailer Error: {$mail->ErrorInfo}");
return false;
}
}
public function deleteFeedback($index) {
if (!file_exists($this->feedbackFile)) return false;
$feedbacks = json_decode(file_get_contents($this->feedbackFile), true);
if (isset($feedbacks[$index])) {
unset($feedbacks[$index]);
$feedbacks = array_values($feedbacks);
file_put_contents($this->feedbackFile, json_encode($feedbacks, JSON_PRETTY_PRINT));
return true;
}
return false;
}
private function getEmailTemplate($name, $email, $message, $date, $ip) {
return "
👤 Name: {$name}
💬 Nachricht:
" . nl2br($message) . "
";
}
}
class AdminManager {
public function isAdmin() {
return isset($_SESSION['admin']) && $_SESSION['admin'] === true;
}
public function handleLogin($username, $password) {
if ($username === 'admin' && $password === 'sonne4000$$$$Q') {
$_SESSION['admin'] = true;
return true;
}
return false;
}
public function handleImageUpload($file) {
if (!$this->isAdmin()) return false;
if (!isset($file['tmp_name']) || empty($file['tmp_name'])) { echo "Keine Datei."; return false; }
$target_dir = "uploads/";
if (!file_exists($target_dir)) mkdir($target_dir, 0777, true);
$target_file = $target_dir . basename($file["name"]);
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
$check = @getimagesize($file["tmp_name"]);
if($check === false) { echo "Kein Bild."; return false; }
if ($file["size"] > 5000000) { echo "Zu groß (>5MB)."; return false; }
if(!in_array($imageFileType, ['jpg','png','jpeg','gif'])) { echo "Falsches Format."; return false; }
if (move_uploaded_file($file["tmp_name"], $target_file)) {
echo "
✅ Bild erfolgreich hochgeladen.
";
return true;
} else {
echo "Upload Fehler.";
return false;
}
}
public function displayLoginForm() {
return '
Benutzername:
Passwort:
Einloggen
';
}
public function displayAdminContent() {
global $settingsManager, $siteConfig;
$feedbacks = json_decode(file_get_contents('feedbacks.json') ?: '[]', true);
// NEUES SETTINGS PANEL
$output = '
';
$output .= '
⚙️ Anzeige-Einstellungen ';
// Zuschauer-Anzeige Settings
$output .= '
';
$output .= '
👥 Zuschauer-Anzeige ';
$output .= '
';
$output .= '
Zuschauer-Anzahl anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('viewer_display.enabled') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Mindestanzahl für Anzeige ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
// Video-Modus Settings
$output .= '
';
$output .= '
🎬 Video-Modus ';
$output .= '
';
$output .= '
Videos im Player abspielen ';
$output .= '
';
$output .= '';
$output .= ' get('video_mode.play_in_player') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Download erlauben ';
$output .= '
';
$output .= '';
$output .= ' get('video_mode.allow_download') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
// UI Display Settings (Punkt 2)
$output .= '
';
$output .= '
🖼️ UI Anzeige ';
$output .= '
';
$output .= '
Empfehlungs-Banner anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('ui_display.show_recommendation_banner') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
QR-Code Section anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('ui_display.show_qr_code') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Social Media Links anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('ui_display.show_social_media') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Patrouille Suisse Section anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('ui_display.show_patrouille_suisse') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
// Zoom & Timelapse Settings (Punkt 3)
$output .= '
';
$output .= '
🔍 Zoom & Timelapse ';
$output .= '
';
$output .= '
Zoom-Controls anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('zoom_timelapse.show_zoom_controls') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Max Zoom-Level ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Timelapse Rückwärts-Modus ';
$output .= '
';
$output .= '';
$output .= ' get('zoom_timelapse.timelapse_reverse_enabled') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
// Content Management Settings (Punkt 5)
$output .= '
';
$output .= '
📝 Content Management ';
$output .= '
';
$output .= '
Gästebuch aktivieren ';
$output .= '
';
$output .= '';
$output .= ' get('content.guestbook_enabled') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Galerie aktivieren ';
$output .= '
';
$output .= '';
$output .= ' get('content.gallery_enabled') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
KI-Events anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('content.ai_events_enabled') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Max Gästebuch-Einträge ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
// Technical Settings (Punkt 6)
$output .= '
';
$output .= '
⚙️ Technische Einstellungen ';
$output .= '
';
$output .= '
Viewer Update-Intervall (Sekunden) ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Session Timeout (Sekunden) ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
// Theme Settings (Punkt 7)
$output .= '
';
$output .= '
🎨 Theme & Design ';
$output .= '
';
$output .= '
Standard-Theme ';
$output .= '
';
$output .= '';
$currentTheme = $settingsManager->get('theme.default_theme');
$output .= 'Klassisch ';
$output .= 'Alpin ';
$output .= 'Modern ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Theme-Switcher anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('theme.show_theme_switcher') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
// SEO Settings (Punkt 8)
$output .= '
';
$output .= '
🔍 SEO & Meta ';
$output .= '
';
$output .= '
Custom Title (leer = Standard) ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Meta Description ';
$output .= '
';
$output .= '' . htmlspecialchars($settingsManager->get('seo.meta_description')) . ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Meta Keywords ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
// Weather Settings
$output .= '
';
$output .= '
🌤️ Wetter-Widget (Open-Meteo - kostenlos, kein API-Key nötig) ';
$output .= '
';
$output .= '
Wetter-Widget anzeigen ';
$output .= '
';
$output .= '';
$output .= ' get('weather.enabled') ? 'checked' : '') . '>';
$output .= ' ';
$output .= ' ';
$output .= '
';
$output .= '
';
// API-KEY FELD KOMPLETT ENTFERNT
$output .= '
';
$output .= '
Standort (Anzeigename) ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Latitude (Breitengrad) ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Longitude (Längengrad) ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Update-Intervall (Minuten) ';
$output .= '
';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
';
$output .= '
Einheit ';
$output .= '
';
$output .= '';
$currentUnits = $settingsManager->get('weather.units');
$output .= 'Metrisch (°C, km/h) ';
$output .= 'Imperial (°F, mph) ';
$output .= ' ';
$output .= '
';
$output .= '
';
$output .= '
'; // settings-group
$output .= '
'; // admin-settings-panel
// Bestehender Admin-Content
$output .= '
';
$output .= '
📩 Posteingang (Kontaktformular) ';
if (empty($feedbacks)) {
$output .= '
Keine Nachrichten vorhanden.
';
} else {
$output .= '
';
foreach ($feedbacks as $index => $feedback) {
$ip = isset($feedback['ip']) ? $feedback['ip'] : 'Unbekannt';
$output .= "
";
$output .= "
";
$output .= "
";
$output .= "
🗑️ Löschen
";
$output .= "
";
$output .= "
{$feedback['message']}
";
$output .= "
📅 {$feedback['date']} | IP: {$ip} ";
$output .= "
";
}
$output .= '
';
}
$output .= '
';
$output .= '
';
$output .= '
🖼️ Bildergalerie verwalten ';
$output .= $this->displayGalleryImages(true);
$output .= '⬆️ Neues Bild hochladen
';
$output .= '';
$output .= '
';
$output .= '
📲 Social Media Links
Facebook
Instagram
TikTok
Link aktualisieren
';
$output .= '';
return $output;
}
public function displayGalleryImages($isAdmin = false) {
$output = '
';
$files = glob("uploads/*.{jpg,jpeg,png,gif}", GLOB_BRACE);
if ($files) {
foreach($files as $file) {
$filename = basename($file);
$output .= '
';
$output .= '
';
if ($isAdmin) {
$output .= '
X
';
}
$output .= '
';
}
} else {
$output .= '
Keine Bilder in der Galerie.
';
}
$output .= '
';
return $output;
}
public function handleSocialMediaUpdate($platform, $url) {
$socialLinks = json_decode(file_get_contents('social_links.json') ?: '{}', true);
$socialLinks[$platform] = $url;
file_put_contents('social_links.json', json_encode($socialLinks));
}
}
class VideoArchiveManager {
private $videoDir;
private $videoDirs;
private $monthNames;
public function __construct($videoDir = './videos/') {
$this->videoDirs = is_array($videoDir) ? $videoDir : [$videoDir];
$this->videoDir = $this->videoDirs[0];
$this->monthNames = [
'01' => 'Januar', '02' => 'Februar', '03' => 'März', '04' => 'April',
'05' => 'Mai', '06' => 'Juni', '07' => 'Juli', '08' => 'August',
'09' => 'September', '10' => 'Oktober', '11' => 'November', '12' => 'Dezember'
];
}
public function getVideosGroupedByDate() {
global $remoteCache;
$videos = [];
// Lokale Videos direkt (schnell)
foreach (glob($this->videoDirs[0] . 'daily_video_*.mp4') as $video) {
if (preg_match('/daily_video_(\d{8})_\d{6}\.mp4/', basename($video), $matches)) {
$dateStr = $matches[1];
$year = substr($dateStr, 0, 4);
$month = substr($dateStr, 4, 2);
$day = substr($dateStr, 6, 2);
$videos[$year][$month][] = [
'path' => $video, 'filename' => basename($video),
'day' => $day, 'filesize' => filesize($video), 'modified' => filemtime($video)
];
}
}
// Remote-Videos aus Cache (kein glob auf SMB!)
foreach ($remoteCache->getVideos() as $rv) {
if (preg_match('/daily_video_(\d{8})_\d{6}\.mp4/', $rv['filename'], $matches)) {
$dateStr = $matches[1];
$year = substr($dateStr, 0, 4);
$month = substr($dateStr, 4, 2);
$day = substr($dateStr, 6, 2);
$videos[$year][$month][] = [
'path' => $rv['path'], 'filename' => $rv['filename'],
'day' => $day, 'filesize' => $rv['filesize'], 'modified' => $rv['mtime']
];
}
}
foreach ($videos as $year => $months) {
foreach ($months as $month => $days) {
usort($videos[$year][$month], function($a, $b) {
return $b['day'] - $a['day'];
});
}
}
return $videos;
}
public function handleSpecificVideoDownload() {
if (isset($_GET['download_specific_video']) && isset($_GET['token'])) {
$videoPath = $_GET['download_specific_video'];
$token = $_GET['token'];
$expectedToken = hash_hmac('sha256', $videoPath, session_id());
if (!hash_equals($expectedToken, $token)) {
echo "Ungültiger Token. Zugriff verweigert.";
exit;
}
$validPath = false;
foreach ($this->videoDirs as $vDir) {
$resolvedDir = realpath($vDir);
$requestedPath = realpath($videoPath);
if ($requestedPath && $resolvedDir && strpos($requestedPath, $resolvedDir) === 0) {
$validPath = true;
break;
}
}
if ($validPath && file_exists($videoPath)) {
$extension = pathinfo($videoPath, PATHINFO_EXTENSION); // ← DAS FEHLT
if (strtolower($extension) !== 'mp4') {
echo "Nur MP4-Dateien können heruntergeladen werden.";
exit;
}
header('Content-Description: File Transfer');
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="'.basename($requestedPath).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($requestedPath));
readfile($requestedPath);
exit;
} else {
echo "Datei nicht gefunden oder ungültiger Dateipfad.";
exit;
}
}
}
}
$webcamManager = new WebcamManager();
$imageFilesJson = $webcamManager->getImageFiles();
$guestbookManager = new GuestbookManager();
$contactManager = new ContactManager();
$adminManager = new AdminManager();
$videoArchiveManager = new VideoArchiveManager(['./videos/', '/mnt/aurora-remote/videos/']);
$videoArchiveManager->handleSpecificVideoDownload();
// AI-Video Download Handler
if (isset($_GET['download_ai_video']) && isset($_GET['token'])) {
$videoPath = $_GET['download_ai_video'];
$token = $_GET['token'];
$expectedToken = hash_hmac('sha256', $videoPath, session_id());
if (hash_equals($expectedToken, $token)) {
$aiDir = realpath('./ai/');
$requestedPath = realpath($videoPath);
if ($requestedPath && strpos($requestedPath, $aiDir) === 0 && file_exists($requestedPath)) {
header('Content-Description: File Transfer');
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="'.basename($requestedPath).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($requestedPath));
readfile($requestedPath);
exit;
}
}
echo "Download fehlgeschlagen.";
exit;
}
if (isset($_GET['action'])) {
switch ($_GET['action']) {
case 'snapshot':
$webcamManager->captureSnapshot();
break;
case 'sequence':
$webcamManager->captureVideoSequence();
break;
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Viewer Heartbeat
if (isset($_POST['action']) && $_POST['action'] === 'viewer_heartbeat') {
$viewerCounter->handleHeartbeat();
}
// GÄSTEBUCH
if (isset($_POST['guestbook'])) {
$guestbookManager->handleFormSubmission();
header("Location: " . $_SERVER['PHP_SELF'] . "#guestbook");
exit;
}
// KONTAKTFORMULAR
elseif (isset($_POST['contact'])) {
$result = $contactManager->handleSubmission($_POST['name'], $_POST['email'], $_POST['message']);
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
header('Content-Type: application/json');
echo json_encode($result);
exit;
}
$_SESSION['contact_result'] = $result;
header('Location: ' . $_SERVER['PHP_SELF'] . '#kontakt');
exit;
}
// ADMIN LOGIN
elseif (isset($_POST['admin-login'])) {
$adminManager->handleLogin($_POST['username'], $_POST['password']);
header('Location: ' . $_SERVER['PHP_SELF'] . '#admin');
exit;
}
// ADMIN AKTIONEN
elseif ($adminManager->isAdmin()) {
if (isset($_POST['action']) && $_POST['action'] === 'delete_guestbook' && isset($_POST['delete_entry'])) {
$guestbookManager->deleteEntry(intval($_POST['delete_entry']));
header("Location: " . $_SERVER['PHP_SELF'] . "#guestbook");
exit;
}
if (isset($_POST['action']) && $_POST['action'] === 'delete_feedback' && isset($_POST['delete_index'])) {
$contactManager->deleteFeedback(intval($_POST['delete_index']));
header("Location: " . $_SERVER['PHP_SELF'] . "#admin");
exit;
}
if (isset($_POST['action']) && $_POST['action'] === 'delete_image' && isset($_POST['image_name'])) {
$filename = basename($_POST['image_name']);
$filepath = "uploads/" . $filename;
if (file_exists($filepath)) { unlink($filepath); }
header("Location: " . $_SERVER['PHP_SELF'] . "#admin");
exit;
}
if (isset($_POST['update-social-media'])) {
$adminManager->handleSocialMediaUpdate($_POST['social-platform'], $_POST['social-url']);
header("Location: " . $_SERVER['PHP_SELF'] . "#admin");
exit;
}
if (isset($_FILES["fileToUpload"])) {
$adminManager->handleImageUpload($_FILES["fileToUpload"]);
}
}
}
// Viewer-Anzeige Einstellungen für JavaScript
$viewerCount = $viewerCounter->getInitialCount();
$showViewers = $settingsManager->shouldShowViewers($viewerCount);
$minViewersToShow = $settingsManager->get('viewer_display.min_viewers');
?>
getCustomTitle() ?: $siteConfig['siteTitle']; ?>
Erleben Sie faszinierende Ausblicke der Züricher Region - in Echtzeit!
isWeatherEnabled()): ?>
getCurrentWeather();
} catch (Exception $e) {
$weather = ['error' => 'Fehler: ' . $e->getMessage()];
}
if ($weather && !isset($weather['error'])):
?>
⚠️ Wetterdaten nicht verfügbar:
−
1.0x
+
⟲
displayStreamStats(); ?>
Zuschauer
Gefällt dir die Livecam? Unterstütze das Projekt – jeder Franken zählt und hilft die Betriebskosten zu decken. So kannst du auch weiterhin Fotos und Videos kostenlos nutzen.
Unterstütze die Aurora Livecam mit deiner Spende. Du hilfst dabei, Kamera, Server, Starlink-Verbindung und die 100 % autarke Stromversorgung mit Solarenergie und EcoFlow-Speichern zu finanzieren. So bleibt das Projekt nachhaltig, unabhängig und für alle frei zugänglich.
🌱 724 Green Label
Live & 100% autark: Unsere Aurora-Webcam
Powered by EcoFlow & Swiss Sun
Erleben Sie Dürnten in Echtzeit – angetrieben durch die reine Kraft der Natur. Unsere Infrastruktur wird zu 100 % mit Solarstrom und EcoFlow-Batteriespeichern betrieben.
724 Green Label Swiss: Zertifizierte Nachhaltigkeit.
24/7 Green Power: Dank modernster Speichertechnologie.
Emissionsfrei: Innovation direkt aus dem Herzen der Schweiz.
🛰️ Schnelles Internet für Ihre Livestreams
Für hochwertige Webcam-Übertragungen empfehlen wir Starlink –
Highspeed-Internet überall verfügbar, perfekt für abgelegene Standorte!
Videoarchiv Tagesvideos
displayVisualCalendar();
?>
Kamera-Blickrichtung
📍 Ungefährer Standort
⛰️ Höhe: ca. 616 m ü.M.
🧭 Blickrichtung: Südwest (SW)
📍 Region: Zürich Oberland
Folge uns und kopiere den Code und sende es deinen Freunden
Klicke auf den QR-Code, um die URL zu kopieren
Gästebuch
displayForm();
echo $guestbookManager->displayEntries($adminManager->isAdmin());
?>
Kontakt
Haben Sie Fragen, Anregungen oder möchten uns unterstützen? Wir freuen uns auf Ihre Nachricht!
displayForm(); ?>
Bildergalerie
displayGalleryImages(); ?>
Über unser Projekt
Dazu betreiben wir seit 2010 rund um die Uhr hochauflösende Webcams. Besonders stolz sind wir auf einzigartige Einblicke, wie z.B. die Trainingsflüge der Patrouille Suisse jeden Montagmorgen.
isAdmin()): ?>
Admin-Bereich
displayAdminContent(); ?>
Admin Login
displayLoginForm(); ?>
Patrouille Suisse Live - Trainingsflüge Beobachten
Jeden Montag Live!
Die Patrouille Suisse, das offizielle Kunstflugteam der Schweizer Luftwaffe, trainiert jeden Montagmorgen in der Region Zürich Oberland. Unsere Webcam bietet einen einzigartigen Blick auf die spektakulären Flugmanöver der sechs F-5E Tiger II Jets.
Trainingszeit: ca. 09:00 - 11:00 Uhr
Bei gutem Wetter sichtbar
Einzigartige Perspektive aus dem Zürcher Oberland
Geschichte der Patrouille Suisse
Gegründet 1964, ist die Patrouille Suisse eines der renommiertesten Kunstflugteams Europas. Das Team fliegt seit 1995 die Northrop F-5E Tiger II und begeistert bei Shows in der ganzen Schweiz und international.
Heimatbasis: Payerne (VD)
Flugzeuge: 6x F-5E Tiger II
Teamgrösse: 6 Piloten + Crew
Beste Beobachtungstipps
Für die beste Sicht auf die Trainingsflüge empfehlen wir:
Nutzen Sie die Zoom-Funktion unserer Webcam
Timelapse-Modus für beschleunigte Ansicht
Tagesvideos zum Nachschauen
AI-Erkennung markiert Flugzeug-Sichtungen
Hinweis: Bei schlechtem Wetter können Trainings abgesagt werden.
Aktuelle Wetter-News, Webcam-Updates und Naturbeobachtungen aus dem Zürcher Oberland
🌅
Sonnenaufgänge über dem Zürichsee
Januar 2024
Die Wintermonate bieten spektakuläre Sonnenaufgänge über dem Zürichsee. Unsere AI-Erkennung identifiziert automatisch die schönsten Momente und speichert sie in der Galerie.
Besonders bei Hochnebel entstehen eindrucksvolle Lichtstimmungen, wenn die Sonne durch die Wolkendecke bricht.
🏔️
Alpenpanorama im Winter
Dezember 2023
An klaren Wintertagen reicht die Sicht von unserer Webcam auf 616m Höhe bis zu den schneebedeckten Gipfeln der Glarner Alpen. Säntis, Glärnisch und weitere Bergspitzen sind sichtbar.
Nutzen Sie die Zoom-Funktion für detaillierte Ansichten der Berglandschaft.
✈️
Patrouille Suisse Saison 2024
März 2024
Die neue Flugsaison der Patrouille Suisse hat begonnen! Jeden Montag trainiert das Kunstflugteam über dem Zürcher Oberland - unsere Webcam fängt die Flugmanöver live ein.
Die AI-Erkennung markiert Flugzeug-Sichtungen automatisch in unserer Galerie.
Weitere Wetter-Updates und Beobachtungen finden Sie auf unseren Social Media Kanälen.
Impressum
M. Kessler
Dürnten, Schweiz
Anfragen per Kontaktformular
Gefällt dir die Livecam? Unterstütze das Projekt – jeder Franken zählt und hilft die Betriebskosten zu decken. So kannst du auch weiterhin Fotos und Videos kostenlos nutzen.
isEmailSharingEnabled()): ?>
📤 Per E-Mail teilen
×
Dein Name
E-Mail-Adresse des Empfängers *
Nachricht (optional)
Abbrechen
📧 Senden
isAdmin()): ?>