Willkommen bei Aurora Wetter Livecam
Erleben Sie faszinierende Ausblicke der Züricher Region - in Echtzeit!
handleAjax(); if (isset($_GET['download_video'])) { $videoDir = './videos/'; $latestVideo = null; $latestTime = 0; // Finde das neueste Video foreach (glob($videoDir . '*.mp4') as $video) { $mtime = filemtime($video); if ($mtime > $latestTime) { $latestTime = $mtime; $latestVideo = $video; } } if ($latestVideo) { header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($latestVideo).'"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($latestVideo)); readfile($latestVideo); exit; } else { echo "Kein Video zum Herunterladen gefunden."; exit; } } // Funktion zur sicheren Umleitung function safeRedirect($url) { if (!headers_sent()) { header("HTTP/1.1 301 Moved Permanently"); header("Location: " . $url); } else { echo ''; } exit(); } // Hauptlogik $oldDomains = [ 'www.aurora-wetter-lifecam.ch', 'www.aurora-wetter-livecam.ch' ]; $newDomain = 'www.aurora-weather-livecam.com'; if (in_array($_SERVER['HTTP_HOST'], $oldDomains)) { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'; $newUrl = $protocol . '://' . $newDomain . $_SERVER['REQUEST_URI']; // Logging für Debugging error_log("Umleitung von {$_SERVER['HTTP_HOST']} nach $newUrl"); if (!headers_sent()) { header("HTTP/1.1 301 Moved Permanently"); header("Location: " . $newUrl); } else { echo ''; } exit(); } session_start(); error_reporting(E_ALL); ini_set('display_errors', 1); $imageDir = "./image"; // Angepasst an das Ausgabeverzeichnis des Bash-Skripts $imageFiles = glob("$imageDir/screenshot_*.jpg"); rsort($imageFiles); // Sortiert die Dateien in umgekehrter Reihenfolge (neueste zuerst) $imageFilesJson = json_encode($imageFiles); class ViewerCounter { private $file = 'active_viewers.json'; private $timeout = 30; // Zeit in Sekunden, bis ein User als "offline" gilt public function handleHeartbeat() { $ip = md5($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']); // Anonymisierte ID $now = time(); $viewers = []; // 1. Datei lesen (mit Lock für Sicherheit bei vielen Zugriffen) if (file_exists($this->file)) { $content = file_get_contents($this->file); $viewers = json_decode($content, true) ?? []; } // 2. Aktuellen User updaten $viewers[$ip] = $now; // 3. Alte User entfernen & Zählen $activeCount = 0; $newViewers = []; foreach ($viewers as $userIp => $lastSeen) { if ($now - $lastSeen < $this->timeout) { $newViewers[$userIp] = $lastSeen; $activeCount++; } } // 4. Speichern file_put_contents($this->file, json_encode($newViewers)); // 5. Ergebnis zurückgeben header('Content-Type: application/json'); echo json_encode(['count' => $activeCount]); exit; } public function getInitialCount() { if (file_exists($this->file)) { $viewers = json_decode(file_get_contents($this->file), true) ?? []; // Nur grob zählen, genaues Update macht das JS sofort nach Laden return count($viewers); } return 1; // Zumindest man selbst ist da } } // Instanz erstellen $viewerCounter = new ViewerCounter(); class WebcamManager { private $videoSrc = 'test_video.m3u8'; private $logoPath = 'logo.png'; // Zeigt NUR das Video ohne Schnickschnack public function displayWebcam() { return ' '; } // Das ist die neue Anzeige für unten links public function displayStreamStats() { return '
'; } public function captureSnapshot() { $outputFile = 'snapshot_' . date('YmdHis') . '.jpg'; $src = escapeshellarg($this->videoSrc); $logo = escapeshellarg($this->logoPath); $command = "ffmpeg -i {$src} -i {$logo} -filter_complex 'overlay=main_w-overlay_w-10:10' -vframes 1 -q:v 2 {$outputFile}"; exec($command, $output, $returnVar); if ($returnVar !== 0) return "Fehler"; $uploadDir = "uploads/"; if (!file_exists($uploadDir)) mkdir($uploadDir, 0777, true); $uploadFile = $uploadDir . $outputFile; copy($outputFile, $uploadFile); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $outputFile . '"'); readfile($outputFile); unlink($outputFile); exit; } public function getImageFiles() { $imageFiles = glob("image/screenshot_*.jpg"); if ($imageFiles) rsort($imageFiles); else $imageFiles = []; return json_encode($imageFiles); } public function captureVideoSequence($duration = 10) { $outputFile = 'sequence_' . date('YmdHis') . '.mp4'; $src = escapeshellarg($this->videoSrc); $logo = escapeshellarg($this->logoPath); $dur = intval($duration); $command = "ffmpeg -i {$src} -i {$logo} -filter_complex 'overlay=10:10' -t {$dur} -c:v libx264 -preset fast -crf 23 {$outputFile}"; exec($command, $output, $returnVar); if ($returnVar !== 0) return "Fehler"; $uploadDir = "uploads/"; if (!file_exists($uploadDir)) mkdir($uploadDir, 0777, true); $uploadFile = $uploadDir . $outputFile; copy($outputFile, $uploadFile); header('Content-Type: video/mp4'); header('Content-Disposition: attachment; filename="' . $outputFile . '"'); readfile($outputFile); unlink($outputFile); exit; } public function getJavaScript() { return " document.addEventListener('DOMContentLoaded', function () { var video = document.getElementById('webcam-player'); var videoSrc = '{$this->videoSrc}'; var bitrateBadge = document.getElementById('bitrate-display'); var bitrateValue = document.getElementById('bitrate-value'); var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); var isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent); console.log('Webcam Player Init - videoSrc:', videoSrc); if(video) { video.controls = false; // Native HLS Unterstützung prüfen (Safari, iOS) var canPlayHls = video.canPlayType('application/vnd.apple.mpegurl') !== ''; if (isIOS || canPlayHls) { // Native HLS (Safari, iOS) console.log('Using native HLS'); video.src = videoSrc; video.setAttribute('playsinline', ''); video.setAttribute('webkit-playsinline', ''); video.muted = true; if(bitrateBadge) bitrateBadge.style.display = 'none'; video.addEventListener('loadedmetadata', function() { console.log('Video metadata loaded'); video.play().catch(function(e) { console.log('Play error:', e); }); }); video.addEventListener('error', function(e) { console.error('Video error:', e); }); } else if (typeof Hls !== 'undefined' && Hls.isSupported()) { // HLS.js für andere Browser console.log('Using HLS.js'); var hls = new Hls({ enableWorker: !isMobile, lowLatencyMode: false, debug: false }); hls.loadSource(videoSrc); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, function () { console.log('HLS manifest parsed'); if (isMobile) video.muted = true; video.play().catch(function(e) { console.log('Play error:', e); }); if(bitrateBadge) bitrateBadge.style.display = 'inline-flex'; }); hls.on(Hls.Events.FRAG_LOADED, function(event, data) { var bandwidth = hls.bandwidthEstimate; if (bandwidth && !isNaN(bandwidth) && bitrateValue) { var mbs = bandwidth / 8 / 1024 / 1024; if (mbs > 0) bitrateValue.textContent = mbs.toFixed(2); } }); hls.on(Hls.Events.ERROR, function(event, data) { console.error('HLS error:', data.type, data.details); }); } else { // Fallback: Versuche direkt zu laden console.log('No HLS support, trying direct load'); video.src = videoSrc; video.muted = true; video.play().catch(function(e) { console.log('Play error:', e); }); } } else { console.error('Video element not found!'); } }); "; } public function setVideoSrc($src) { $this->videoSrc = $src; } } class VisualCalendarManager { private $videoDir; private $aiDir; private $monthNames; private $settingsManager; // AI-Kategorien mit Icons und Farben private $aiCategories = [ 'sunny' => ['icon' => '☀️', 'name' => 'Sonnig', 'color' => '#FFD700'], 'rainy' => ['icon' => '🌧️', 'name' => 'Regen', 'color' => '#4682B4'], 'snowy' => ['icon' => '❄️', 'name' => 'Schnee', 'color' => '#E0FFFF'], 'planes' => ['icon' => '✈️', 'name' => 'Flugzeuge', 'color' => '#87CEEB'], 'birds' => ['icon' => '🐦', 'name' => 'Vögel', 'color' => '#98FB98'], 'sunset' => ['icon' => '🌅', 'name' => 'Sonnenuntergang', 'color' => '#FF6347'], 'sunrise' => ['icon' => '🌄', 'name' => 'Sonnenaufgang', 'color' => '#FFA07A'], 'rainbow' => ['icon' => '🌈', 'name' => 'Regenbogen', 'color' => '#FF69B4'], ]; public function __construct($videoDir = './videos/', $aiDir = './ai/', $settingsManager = null) { $this->videoDir = $videoDir; $this->aiDir = $aiDir; $this->settingsManager = $settingsManager; $this->monthNames = [ 1 => ['de' => 'Januar', 'en' => 'January', 'it' => 'Gennaio', 'fr' => 'Janvier', 'zh' => '一月'], 2 => ['de' => 'Februar', 'en' => 'February', 'it' => 'Febbraio', 'fr' => 'Février', 'zh' => '二月'], 3 => ['de' => 'März', 'en' => 'March', 'it' => 'Marzo', 'fr' => 'Mars', 'zh' => '三月'], 4 => ['de' => 'April', 'en' => 'April', 'it' => 'Aprile', 'fr' => 'Avril', 'zh' => '四月'], 5 => ['de' => 'Mai', 'en' => 'May', 'it' => 'Maggio', 'fr' => 'Mai', 'zh' => '五月'], 6 => ['de' => 'Juni', 'en' => 'June', 'it' => 'Giugno', 'fr' => 'Juin', 'zh' => '六月'], 7 => ['de' => 'Juli', 'en' => 'July', 'it' => 'Luglio', 'fr' => 'Juillet', 'zh' => '七月'], 8 => ['de' => 'August', 'en' => 'August', 'it' => 'Agosto', 'fr' => 'Août', 'zh' => '八月'], 9 => ['de' => 'September', 'en' => 'September', 'it' => 'Settembre', 'fr' => 'Septembre', 'zh' => '九月'], 10 => ['de' => 'Oktober', 'en' => 'October', 'it' => 'Ottobre', 'fr' => 'Octobre', 'zh' => '十月'], 11 => ['de' => 'November', 'en' => 'November', 'it' => 'Novembre', 'fr' => 'Novembre', 'zh' => '十一月'], 12 => ['de' => 'Dezember', 'en' => 'December', 'it' => 'Dicembre', 'fr' => 'Décembre', 'zh' => '十二月'] ]; } /** * Holt AI-Events für ein bestimmtes Datum */ public function getAiEventsForDate($year, $month, $day) { $events = []; $dateStr = sprintf('%04d%02d%02d', $year, $month, $day); foreach ($this->aiCategories as $category => $info) { $categoryDir = $this->aiDir . $category . '/'; if (!is_dir($categoryDir)) continue; // Suche nach Videos für dieses Datum $pattern = $categoryDir . "{$category}_{$dateStr}*.mp4"; $videos = glob($pattern); if (!empty($videos)) { $events[$category] = [ 'icon' => $info['icon'], 'name' => $info['name'], 'color' => $info['color'], 'videos' => $videos, 'count' => count($videos) ]; } } return $events; } /** * Prüft ob AI-Events für ein Datum existieren */ public function hasAiEventsForDate($year, $month, $day) { $dateStr = sprintf('%04d%02d%02d', $year, $month, $day); foreach (array_keys($this->aiCategories) as $category) { $pattern = $this->aiDir . $category . "/{$category}_{$dateStr}*.mp4"; if (count(glob($pattern)) > 0) { return true; } } return false; } /** * Holt kurze Icon-Liste für Kalender-Anzeige */ public function getAiIconsForDate($year, $month, $day) { $icons = []; $events = $this->getAiEventsForDate($year, $month, $day); foreach ($events as $category => $info) { $icons[] = $info['icon']; } return $icons; } public function getVideosForDate($year, $month, $day) { $videos = []; $dateStr = sprintf('%04d%02d%02d', $year, $month, $day); foreach (glob($this->videoDir . "daily_video_{$dateStr}_*.mp4") as $video) { $videos[] = [ 'path' => $video, 'filename' => basename($video), 'filesize' => filesize($video), 'time' => date('H:i', filemtime($video)) ]; } return $videos; } public function hasVideosForDate($year, $month, $day) { $dateStr = sprintf('%04d%02d%02d', $year, $month, $day); $pattern = $this->videoDir . "daily_video_{$dateStr}_*.mp4"; return count(glob($pattern)) > 0; } public function displayVisualCalendar() { $currentYear = isset($_GET['cal_year']) ? intval($_GET['cal_year']) : date('Y'); $currentMonth = isset($_GET['cal_month']) ? intval($_GET['cal_month']) : date('n'); $selectedDay = isset($_GET['cal_day']) ? intval($_GET['cal_day']) : null; // Settings für Video-Modus holen $playInPlayer = $this->settingsManager ? $this->settingsManager->get('video_mode.play_in_player') : true; $allowDownload = $this->settingsManager ? $this->settingsManager->get('video_mode.allow_download') : true; $output = '📭 Keine Videos oder AI-Ereignisse für diesen Tag verfügbar.
'; $output .= '{$entry['message']}
{$entry['date']}"; if ($isAdmin) { $output .= ""; } } $output .= 'Aurora Weather Livecam
✅ Bild erfolgreich hochgeladen.
"; return true; } else { echo "Upload Fehler."; return false; } } public function displayLoginForm() { return ' '; } public function displayAdminContent() { global $settingsManager; $feedbacks = json_decode(file_get_contents('feedbacks.json') ?: '[]', true); // NEUES SETTINGS PANEL $output = 'Keine Nachrichten vorhanden.
'; } else { $output .= '{$feedback['message']}
"; $output .= "📅 {$feedback['date']} | IP: {$ip}"; $output .= "Keine Bilder in der Galerie.
'; } $output .= '
Erleben Sie faszinierende Ausblicke der Züricher Region - in Echtzeit!
⛰️ 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
Haben Sie Fragen, Anregungen oder möchten uns unterstützen? Wir freuen uns auf Ihre Nachricht!
displayForm(); ?>Aurora Wetter Livecam ist ein Herzensprojekt von Wetterbegeisterten. Wir möchten Ihnen die Schönheit der Natur und Faszination des Wetters näher bringen.
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.
Aurora Wetter Livecam
M. Kessler
Dürnten, Schweiz
Anfragen per Kontaktformular