diff --git a/aurora-livecam/index.php b/aurora-livecam/index.php
index 04668ba..8573e44 100644
--- a/aurora-livecam/index.php
+++ b/aurora-livecam/index.php
@@ -8,113 +8,216 @@ require_once 'SettingsManager.php';
// SettingsManager initialisieren
$settingsManager = new SettingsManager();
-// AJAX-Handler für Settings (MUSS ganz am Anfang sein!)
-if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['settings_action'])) {
- header('Content-Type: application/json');
-
- switch ($_POST['settings_action']) {
- case 'get':
- echo json_encode(['success' => true, 'settings' => $settingsManager->get()]);
- exit;
-
- case 'update':
- $key = $_POST['key'] ?? null;
- $value = $_POST['value'] ?? null;
-
- if ($value === 'true') $value = true;
- if ($value === 'false') $value = false;
- if (is_numeric($value)) $value = intval($value);
-
- if ($key && $settingsManager->set($key, $value)) {
- echo json_encode(['success' => true, 'message' => 'Gespeichert']);
- } else {
- echo json_encode(['success' => false, 'message' => 'Fehler']);
- }
- exit;
- }
-}
+// AJAX-Handler für Settings (VOR anderen Ausgaben!)
+$settingsManager->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 ($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;
}
- echo "Kein Video gefunden.";
- exit;
}
-$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';
- header("HTTP/1.1 301 Moved Permanently");
- header("Location: " . $protocol . '://' . $newDomain . $_SERVER['REQUEST_URI']);
- 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', 0);
-
-$imageDir = "./image";
+ini_set('display_errors', 1);
+$imageDir = "./image"; // Angepasst an das Ausgabeverzeichnis des Bash-Skripts
$imageFiles = glob("$imageDir/screenshot_*.jpg");
-if ($imageFiles) rsort($imageFiles);
-$imageFilesJson = json_encode($imageFiles ?: []);
+rsort($imageFiles); // Sortiert die Dateien in umgekehrter Reihenfolge (neueste zuerst)
+$imageFilesJson = json_encode($imageFiles);
+
class ViewerCounter {
private $file = 'active_viewers.json';
- private $timeout = 30;
+ private $timeout = 30; // Zeit in Sekunden, bis ein User als "offline" gilt
public function handleHeartbeat() {
- $ip = md5($_SERVER['REMOTE_ADDR'] . ($_SERVER['HTTP_USER_AGENT'] ?? ''));
+ $ip = md5($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']); // Anonymisierte ID
$now = time();
- $viewers = file_exists($this->file) ? json_decode(file_get_contents($this->file), true) ?? [] : [];
+
+ $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;
- $active = [];
- foreach ($viewers as $u => $t) { if ($now - $t < $this->timeout) $active[$u] = $t; }
- file_put_contents($this->file, json_encode($active));
+
+ // 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' => count($active)]);
+ echo json_encode(['count' => $activeCount]);
exit;
}
public function getInitialCount() {
if (file_exists($this->file)) {
- return max(1, count(json_decode(file_get_contents($this->file), true) ?? []));
+ $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;
+ 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 ' ';
+ return '
+
+ ';
}
+ // Das ist die neue Anzeige für unten links
public function displayStreamStats() {
- return '
-
0.00 MBit/s
+ return '
+
+
+ Stream: 0.00 MBit/s
';
}
+ 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() {
- $f = glob("image/screenshot_*.jpg");
- if ($f) rsort($f);
- return json_encode($f ?: []);
+ $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() {
@@ -122,600 +225,2594 @@ class WebcamManager {
document.addEventListener('DOMContentLoaded', function () {
var video = document.getElementById('webcam-player');
var videoSrc = '{$this->videoSrc}';
- if(video && typeof Hls !== 'undefined' && Hls.isSupported()) {
- var hls = new Hls();
- hls.loadSource(videoSrc);
- hls.attachMedia(video);
- hls.on(Hls.Events.MANIFEST_PARSED, function () { video.play().catch(()=>{}); });
- } else if (video) {
- video.src = videoSrc;
- video.play().catch(()=>{});
+ 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);
+
+ if(video) {
+ video.controls = false;
+ if (isIOS) {
+ video.src = videoSrc;
+ video.setAttribute('playsinline', '');
+ video.setAttribute('webkit-playsinline', '');
+ video.muted = true;
+ if(bitrateBadge) bitrateBadge.style.display = 'none';
+ video.addEventListener('loadedmetadata', function() { video.play().catch(console.log); });
+ } else if (Hls.isSupported()) {
+ var hls = new Hls({ enableWorker: !isMobile, lowLatencyMode: false });
+ hls.loadSource(videoSrc);
+ hls.attachMedia(video);
+ hls.on(Hls.Events.MANIFEST_PARSED, function () {
+ if (isMobile) video.muted = true;
+ video.play().catch(console.log);
+ 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);
+ }
+ });
+ }
}
- });";
+ });
+ ";
}
+
+ public function setVideoSrc($src) { $this->videoSrc = $src; }
}
+
+
class VisualCalendarManager {
- private $videoDir, $settingsManager;
- private $months = [1=>'Jan',2=>'Feb',3=>'Mär',4=>'Apr',5=>'Mai',6=>'Jun',7=>'Jul',8=>'Aug',9=>'Sep',10=>'Okt',11=>'Nov',12=>'Dez'];
+ private $videoDir;
+ private $aiDir;
+ private $monthNames;
+ private $settingsManager;
- public function __construct($videoDir = './videos/', $sm = null) {
+ // 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->settingsManager = $sm;
+ $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' => '十二月']
+ ];
}
- public function hasVideosForDate($y, $m, $d) {
- return count(glob($this->videoDir . sprintf("daily_video_%04d%02d%02d_*.mp4", $y, $m, $d))) > 0;
- }
+ /**
+ * Holt AI-Events für ein bestimmtes Datum
+ */
+ public function getAiEventsForDate($year, $month, $day) {
+ $events = [];
+ $dateStr = sprintf('%04d%02d%02d', $year, $month, $day);
- public function getVideosForDate($y, $m, $d) {
- $vids = [];
- foreach (glob($this->videoDir . sprintf("daily_video_%04d%02d%02d_*.mp4", $y, $m, $d)) as $v) {
- $vids[] = ['path' => $v, 'name' => basename($v), 'size' => filesize($v), 'time' => date('H:i', filemtime($v))];
+ 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 $vids;
+
+ 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() {
- $cy = isset($_GET['cal_year']) ? intval($_GET['cal_year']) : date('Y');
- $cm = isset($_GET['cal_month']) ? intval($_GET['cal_month']) : date('n');
- $sd = isset($_GET['cal_day']) ? intval($_GET['cal_day']) : null;
- $pip = $this->settingsManager ? $this->settingsManager->get('video_mode.play_in_player') : true;
- $dl = $this->settingsManager ? $this->settingsManager->get('video_mode.allow_download') : true;
+ $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;
- $o = '
';
- $o .= '
« '.$this->months[$cm].' '.$cy.' »
';
- $o .= '
';
- foreach(['Mo','Di','Mi','Do','Fr','Sa','So'] as $wd) $o .= '
'.$wd.'
';
+ // 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;
- $fd = mktime(0,0,0,$cm,1,$cy);
- $dim = date('t', $fd);
- $dow = date('N', $fd) - 1;
- for ($i=0; $i<$dow; $i++) $o .= '
';
+ $output = '
';
- for ($d=1; $d<=$dim; $d++) {
- $hv = $this->hasVideosForDate($cy,$cm,$d);
- $sel = $sd==$d;
- $td = ($cy==date('Y') && $cm==date('n') && $d==date('j'));
- $cls = 'cal-day' . ($hv?' has-vid':'') . ($sel?' sel':'') . ($td?' today':'');
- $o .= '
'.$d.' '.($hv?'📹 ':'').'
';
+ // Navigation
+ $output .= '
';
+ $output .= '◀ ';
+ $output .= '
' . $this->monthNames[$currentMonth]['de'] . ' ' . $currentYear . ' ';
+ $output .= '▶ ';
+ $output .= '';
+
+ // AI-Legende
+ $output .= '
';
+ $output .= '🤖 AI-Erkennung: ';
+ foreach ($this->aiCategories as $cat => $info) {
+ $output .= '' . $info['icon'] . ' ';
}
- $o .= '
';
+ $output .= '
';
- if ($sd) {
- $vids = $this->getVideosForDate($cy,$cm,$sd);
- $o .= '
📅 '.sprintf('%02d.%02d.%04d',$sd,$cm,$cy).' ';
- if ($vids) {
- $o .= '
';
- foreach ($vids as $v) {
- $sz = round($v['size']/1024/1024,1);
- $tk = hash_hmac('sha256', $v['path'], session_id());
- $o .= '🕐 '.$v['time'].' '.$sz.' MB ';
- if ($pip) $o .= '▶️ ';
- if ($dl) $o .= '⬇️ ';
- $o .= ' ';
- }
- $o .= ' ';
- } else {
- $o .= '
Keine Videos.
';
+ // 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 .= '📹 ';
}
- $o .= '
';
- }
- $o .= '
';
- return $o;
- }
-}
-class GuestbookManager {
- private $entries = [], $file = 'guestbook.json';
- public function __construct() { if (file_exists($this->file)) $this->entries = json_decode(file_get_contents($this->file), true) ?? []; }
- public function handleFormSubmission() {
- if (isset($_POST['guestbook'],$_POST['guest-name'],$_POST['guest-message'])) {
- $this->entries[] = ['name'=>htmlspecialchars($_POST['guest-name']),'message'=>htmlspecialchars($_POST['guest-message']),'date'=>date('Y-m-d H:i:s')];
- file_put_contents($this->file, json_encode($this->entries));
- }
- }
- public function deleteEntry($i) { if (isset($this->entries[$i])) { unset($this->entries[$i]); $this->entries = array_values($this->entries); file_put_contents($this->file, json_encode($this->entries)); return true; } return false; }
- public function displayForm() { return '
'; }
- public function displayEntries($admin=false) {
- $o = '
';
- foreach ($this->entries as $i=>$e) {
- $o .= '
'.$e['name'].' '.$e['message'].'
'.$e['date'].' ';
- if ($admin) $o .= '
X ';
- $o .= '
';
- }
- return $o.'
';
- }
-}
+ // 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 .= '
';
+ }
-class ContactManager {
- private $file = 'feedbacks.json';
- public function displayForm() { return '
Name: E-Mail: Nachricht: Senden
'; }
- public function handleSubmission($n,$e,$m) {
- if (!$n||!$e||!$m) return ['success'=>false,'message'=>'Alle Felder ausfüllen'];
- $fb = ['name'=>htmlspecialchars($n),'email'=>filter_var($e,FILTER_SANITIZE_EMAIL),'message'=>htmlspecialchars($m),'date'=>date('Y-m-d H:i:s'),'ip'=>$_SERVER['REMOTE_ADDR']??''];
- $all = file_exists($this->file) ? json_decode(file_get_contents($this->file),true) : [];
- $all[] = $fb;
- file_put_contents($this->file, json_encode($all, JSON_PRETTY_PRINT));
- return ['success'=>true,'message'=>'Nachricht gesendet!'];
- }
- public function deleteFeedback($i) { $all = json_decode(file_get_contents($this->file),true); if (isset($all[$i])) { unset($all[$i]); file_put_contents($this->file, json_encode(array_values($all),JSON_PRETTY_PRINT)); return true; } return false; }
-}
-
-class AdminManager {
- public function isAdmin() { return isset($_SESSION['admin']) && $_SESSION['admin'] === true; }
- public function handleLogin($u,$p) { if ($u==='admin' && $p==='sonne4000$$$$Q') { $_SESSION['admin']=true; return true; } return false; }
- public function displayLoginForm() { return '
User: Pass: Login '; }
- public function displayAdminContent() {
- global $settingsManager;
- $o = '
';
- $o .= '
📩 Nachrichten ';
- $msgs = file_exists('feedbacks.json') ? json_decode(file_get_contents('feedbacks.json'),true) : [];
- foreach ($msgs as $i=>$m) {
- $o .= '
'.$m['name'].' ('.$m['email'].')
'.$m['message'].'
'.$m['date'].' ';
- $o .= '
X ';
+ $output .= '
';
}
- if (!$msgs) $o .= '
Keine Nachrichten.
';
- $o .= '
';
- return $o;
- }
- public function displayGalleryImages() {
- $o = '
';
- foreach (glob("uploads/*.{jpg,jpeg,png,gif}",GLOB_BRACE) as $f) $o .= '
';
- return $o.'
';
- }
-}
-class VideoArchiveManager {
- private $dir;
- public function __construct($d='./videos/') { $this->dir = $d; }
- public function handleSpecificVideoDownload() {
- if (isset($_GET['download_specific_video'],$_GET['token'])) {
- $p = $_GET['download_specific_video'];
- if (!hash_equals(hash_hmac('sha256',$p,session_id()), $_GET['token'])) { echo "Invalid"; exit; }
- $rp = realpath($p);
- $rd = realpath($this->dir);
- if ($rp && strpos($rp,$rd)===0 && file_exists($rp)) {
- header('Content-Type: video/mp4');
- header('Content-Disposition: attachment; filename="'.basename($rp).'"');
- header('Content-Length: '.filesize($rp));
- readfile($rp);
+ $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;
+
+ $output .= '';
+ $output .= '🕐 ' . $video['time'] . ' Uhr ';
+ $output .= '' . $sizeInMb . ' MB ';
+ $output .= '';
+ $output .= ' ';
+ }
+
+ $output .= ' ';
+ $output .= '
';
+ }
+
+ // === AI-EREIGNISSE ===
+ if (!empty($aiEvents)) {
+ $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;
}
- echo "Not found"; 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 '
+
+
+
+
+ Name:
+
+
+
+ Nachricht:
+
+
+
+ Eintrag hinzufügen
+
+
+ ';
+ }
+public function displayEntries($isAdmin = false) {
+ $output = '
';
+ foreach ($this->entries as $index => $entry) {
+ $output .= "
+
+
{$entry['name']}
+
{$entry['message']}
+
{$entry['date']}";
+ if ($isAdmin) {
+ $output .= "
+
+
+ Löschen
+ ";
+ }
+
+ }
+ $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 '
+
+
+
+ Name:
+
+
+
+
+ E-Mail:
+
+
+
+
+ Nachricht:
+
+
+
+
+ Nachricht senden
+
+
+
';
+ }
+
+ 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;
+
+ $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
+
+ $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 $monthNames;
+
+ public function __construct($videoDir = './videos/') {
+ $this->videoDir = $videoDir;
+ $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() {
+ $videos = [];
+
+ foreach (glob($this->videoDir . '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)
+ ];
+ }
+ }
+
+ 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;
+ }
+
+ $videoDir = realpath($this->videoDir);
+ $requestedPath = realpath($videoPath);
+
+ if ($requestedPath && strpos($requestedPath, $videoDir) === 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;
+ }
+ }
+ }
+}
+
+
$webcamManager = new WebcamManager();
+$imageFilesJson = $webcamManager->getImageFiles();
$guestbookManager = new GuestbookManager();
$contactManager = new ContactManager();
$adminManager = new AdminManager();
+
$videoArchiveManager = new VideoArchiveManager('./videos/');
$videoArchiveManager->handleSpecificVideoDownload();
-if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- if (isset($_POST['action']) && $_POST['action'] === 'viewer_heartbeat') $viewerCounter->handleHeartbeat();
- if (isset($_POST['guestbook'])) { $guestbookManager->handleFormSubmission(); header("Location: ".$_SERVER['PHP_SELF']."#guestbook"); exit; }
- if (isset($_POST['contact'])) {
- $r = $contactManager->handleSubmission($_POST['name'],$_POST['email'],$_POST['message']);
- if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])) { header('Content-Type: application/json'); echo json_encode($r); exit; }
- header('Location: '.$_SERVER['PHP_SELF'].'#kontakt'); exit;
+// 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;
+ }
}
- if (isset($_POST['admin-login'])) { $adminManager->handleLogin($_POST['username'],$_POST['password']); header('Location: '.$_SERVER['PHP_SELF'].'#admin'); exit; }
- if ($adminManager->isAdmin()) {
- if (isset($_POST['action']) && $_POST['action']==='delete_guestbook') { $guestbookManager->deleteEntry(intval($_POST['delete_entry'])); header("Location: ".$_SERVER['PHP_SELF']."#guestbook"); exit; }
- if (isset($_POST['action']) && $_POST['action']==='delete_feedback') { $contactManager->deleteFeedback(intval($_POST['delete_index'])); header("Location: ".$_SERVER['PHP_SELF']."#admin"); exit; }
+ echo "Download fehlgeschlagen.";
+ exit;
+}
+
+if (isset($_GET['action'])) {
+ switch ($_GET['action']) {
+ case 'snapshot':
+ $webcamManager->captureSnapshot();
+ break;
+ case 'sequence':
+ $webcamManager->captureVideoSequence();
+ break;
}
}
-$vc = $viewerCounter->getInitialCount();
-$sv = $settingsManager->get('viewer_display.enabled') && $vc >= $settingsManager->get('viewer_display.min_viewers');
-$mv = $settingsManager->get('viewer_display.min_viewers');
-?>
+
+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');
+?>
+
+
+
+
-
-
-
Aurora Livecam
-
-
+
+
+
+
Aurora Livecam Zürich Oberland | Live Wetter Webcam Schweiz 24/7
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-Aurora Wetter Livecam
-Faszinierende Ausblicke aus dem Zürcher Oberland
-
-
-
-
-
-
-displayWebcam(); ?>
-
-
-
-−
-⟲
-+
-
-
-
-
-
-
-
-
-
---:--:--
-1x
- Live
-
-
-
-
Zurück zu Live
-
-
-displayStreamStats(); ?>
-
Zuschauer
-
-
-
-
+
+
+
+
+
+
+ Willkommen bei Aurora Wetter Livecam
+
+
+
+
+ Erleben Sie faszinierende Ausblicke der Züricher Region - in Echtzeit!
+
+
+
+
+
+
+
Unsere Empfehlungen
+
+
+
+
+
+
+
+
+
+
+ displayWebcam(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ −
+ Zoom
+
+ 1x
+ +
+ ⟲
+
+
+
+
+
+
+
+ displayStreamStats(); ?>
+
+
+
+
+
+ Zuschauer
+
+
+
+
+
+
+
+
+
+
-
-
📅 Videoarchiv
-displayVisualCalendar(); ?>
-
+
+
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()); ?>
-
+
+
Gästebuch
+ displayForm();
+ echo $guestbookManager->displayEntries($adminManager->isAdmin());
+ ?>
+
+
-
-
Kontakt
-displayForm(); ?>
-
+
+
Kontakt
+
+ Haben Sie Fragen, Anregungen oder möchten uns unterstützen? Wir freuen uns auf Ihre Nachricht!
+
+ displayForm(); ?>
+
+
-
-
Galerie
-displayGalleryImages(); ?>
-
+
+
Bildergalerie
+
+
+ displayGalleryImages(); ?>
+
+
+
-isAdmin()): ?>
+
+
+
+
Über unser Projekt
+
+
+
+ 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.
+
+
+
+
+
+
+isAdmin()): ?>
-
-
⚙️ Admin
-displayAdminContent(); ?>
-
+
+
Admin-Bereich
+ displayAdminContent(); ?>
+
-
-
-
Admin Login
-displayLoginForm(); ?>
-
+
+
+
Admin Login
+ displayLoginForm(); ?>
+
+
+
+
+
Impressum
+
Aurora Wetter Livecam
+
M. Kessler
+
Dürnten, Schweiz
+
Anfragen per Kontaktformular
+
+
+
+
+
-
-
×
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+isAdmin()): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aurora-livecam/indexmiau.php b/aurora-livecam/indexmiau.php
new file mode 100644
index 0000000..04668ba
--- /dev/null
+++ b/aurora-livecam/indexmiau.php
@@ -0,0 +1,721 @@
+ true, 'settings' => $settingsManager->get()]);
+ exit;
+
+ case 'update':
+ $key = $_POST['key'] ?? null;
+ $value = $_POST['value'] ?? null;
+
+ if ($value === 'true') $value = true;
+ if ($value === 'false') $value = false;
+ if (is_numeric($value)) $value = intval($value);
+
+ if ($key && $settingsManager->set($key, $value)) {
+ echo json_encode(['success' => true, 'message' => 'Gespeichert']);
+ } else {
+ echo json_encode(['success' => false, 'message' => 'Fehler']);
+ }
+ exit;
+ }
+}
+
+if (isset($_GET['download_video'])) {
+ $videoDir = './videos/';
+ $latestVideo = null;
+ $latestTime = 0;
+ foreach (glob($videoDir . '*.mp4') as $video) {
+ $mtime = filemtime($video);
+ if ($mtime > $latestTime) { $latestTime = $mtime; $latestVideo = $video; }
+ }
+ if ($latestVideo) {
+ header('Content-Type: application/octet-stream');
+ header('Content-Disposition: attachment; filename="'.basename($latestVideo).'"');
+ header('Content-Length: ' . filesize($latestVideo));
+ readfile($latestVideo);
+ exit;
+ }
+ echo "Kein Video gefunden.";
+ exit;
+}
+
+$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';
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: " . $protocol . '://' . $newDomain . $_SERVER['REQUEST_URI']);
+ exit;
+}
+
+session_start();
+error_reporting(E_ALL);
+ini_set('display_errors', 0);
+
+$imageDir = "./image";
+$imageFiles = glob("$imageDir/screenshot_*.jpg");
+if ($imageFiles) rsort($imageFiles);
+$imageFilesJson = json_encode($imageFiles ?: []);
+
+class ViewerCounter {
+ private $file = 'active_viewers.json';
+ private $timeout = 30;
+
+ public function handleHeartbeat() {
+ $ip = md5($_SERVER['REMOTE_ADDR'] . ($_SERVER['HTTP_USER_AGENT'] ?? ''));
+ $now = time();
+ $viewers = file_exists($this->file) ? json_decode(file_get_contents($this->file), true) ?? [] : [];
+ $viewers[$ip] = $now;
+ $active = [];
+ foreach ($viewers as $u => $t) { if ($now - $t < $this->timeout) $active[$u] = $t; }
+ file_put_contents($this->file, json_encode($active));
+ header('Content-Type: application/json');
+ echo json_encode(['count' => count($active)]);
+ exit;
+ }
+
+ public function getInitialCount() {
+ if (file_exists($this->file)) {
+ return max(1, count(json_decode(file_get_contents($this->file), true) ?? []));
+ }
+ return 1;
+ }
+}
+
+$viewerCounter = new ViewerCounter();
+
+class WebcamManager {
+ private $videoSrc = 'test_video.m3u8';
+
+ public function displayWebcam() {
+ return '
';
+ }
+
+ public function displayStreamStats() {
+ return '
+ 0.00 MBit/s
+
';
+ }
+
+ public function getImageFiles() {
+ $f = glob("image/screenshot_*.jpg");
+ if ($f) rsort($f);
+ return json_encode($f ?: []);
+ }
+
+ public function getJavaScript() {
+ return "
+ document.addEventListener('DOMContentLoaded', function () {
+ var video = document.getElementById('webcam-player');
+ var videoSrc = '{$this->videoSrc}';
+ if(video && typeof Hls !== 'undefined' && Hls.isSupported()) {
+ var hls = new Hls();
+ hls.loadSource(videoSrc);
+ hls.attachMedia(video);
+ hls.on(Hls.Events.MANIFEST_PARSED, function () { video.play().catch(()=>{}); });
+ } else if (video) {
+ video.src = videoSrc;
+ video.play().catch(()=>{});
+ }
+ });";
+ }
+}
+
+class VisualCalendarManager {
+ private $videoDir, $settingsManager;
+ private $months = [1=>'Jan',2=>'Feb',3=>'Mär',4=>'Apr',5=>'Mai',6=>'Jun',7=>'Jul',8=>'Aug',9=>'Sep',10=>'Okt',11=>'Nov',12=>'Dez'];
+
+ public function __construct($videoDir = './videos/', $sm = null) {
+ $this->videoDir = $videoDir;
+ $this->settingsManager = $sm;
+ }
+
+ public function hasVideosForDate($y, $m, $d) {
+ return count(glob($this->videoDir . sprintf("daily_video_%04d%02d%02d_*.mp4", $y, $m, $d))) > 0;
+ }
+
+ public function getVideosForDate($y, $m, $d) {
+ $vids = [];
+ foreach (glob($this->videoDir . sprintf("daily_video_%04d%02d%02d_*.mp4", $y, $m, $d)) as $v) {
+ $vids[] = ['path' => $v, 'name' => basename($v), 'size' => filesize($v), 'time' => date('H:i', filemtime($v))];
+ }
+ return $vids;
+ }
+
+ public function displayVisualCalendar() {
+ $cy = isset($_GET['cal_year']) ? intval($_GET['cal_year']) : date('Y');
+ $cm = isset($_GET['cal_month']) ? intval($_GET['cal_month']) : date('n');
+ $sd = isset($_GET['cal_day']) ? intval($_GET['cal_day']) : null;
+ $pip = $this->settingsManager ? $this->settingsManager->get('video_mode.play_in_player') : true;
+ $dl = $this->settingsManager ? $this->settingsManager->get('video_mode.allow_download') : true;
+
+ $o = '
';
+ $o .= '
« '.$this->months[$cm].' '.$cy.' »
';
+ $o .= '
';
+ foreach(['Mo','Di','Mi','Do','Fr','Sa','So'] as $wd) $o .= '
'.$wd.'
';
+
+ $fd = mktime(0,0,0,$cm,1,$cy);
+ $dim = date('t', $fd);
+ $dow = date('N', $fd) - 1;
+ for ($i=0; $i<$dow; $i++) $o .= '
';
+
+ for ($d=1; $d<=$dim; $d++) {
+ $hv = $this->hasVideosForDate($cy,$cm,$d);
+ $sel = $sd==$d;
+ $td = ($cy==date('Y') && $cm==date('n') && $d==date('j'));
+ $cls = 'cal-day' . ($hv?' has-vid':'') . ($sel?' sel':'') . ($td?' today':'');
+ $o .= '
'.$d.' '.($hv?'📹 ':'').'
';
+ }
+ $o .= '
';
+
+ if ($sd) {
+ $vids = $this->getVideosForDate($cy,$cm,$sd);
+ $o .= '
📅 '.sprintf('%02d.%02d.%04d',$sd,$cm,$cy).' ';
+ if ($vids) {
+ $o .= '
';
+ foreach ($vids as $v) {
+ $sz = round($v['size']/1024/1024,1);
+ $tk = hash_hmac('sha256', $v['path'], session_id());
+ $o .= '🕐 '.$v['time'].' '.$sz.' MB ';
+ if ($pip) $o .= '▶️ ';
+ if ($dl) $o .= '⬇️ ';
+ $o .= ' ';
+ }
+ $o .= ' ';
+ } else {
+ $o .= '
Keine Videos.
';
+ }
+ $o .= '
';
+ }
+ $o .= '
';
+ return $o;
+ }
+}
+
+class GuestbookManager {
+ private $entries = [], $file = 'guestbook.json';
+ public function __construct() { if (file_exists($this->file)) $this->entries = json_decode(file_get_contents($this->file), true) ?? []; }
+ public function handleFormSubmission() {
+ if (isset($_POST['guestbook'],$_POST['guest-name'],$_POST['guest-message'])) {
+ $this->entries[] = ['name'=>htmlspecialchars($_POST['guest-name']),'message'=>htmlspecialchars($_POST['guest-message']),'date'=>date('Y-m-d H:i:s')];
+ file_put_contents($this->file, json_encode($this->entries));
+ }
+ }
+ public function deleteEntry($i) { if (isset($this->entries[$i])) { unset($this->entries[$i]); $this->entries = array_values($this->entries); file_put_contents($this->file, json_encode($this->entries)); return true; } return false; }
+ public function displayForm() { return '
Name: Nachricht: Senden '; }
+ public function displayEntries($admin=false) {
+ $o = '
';
+ foreach ($this->entries as $i=>$e) {
+ $o .= '
'.$e['name'].' '.$e['message'].'
'.$e['date'].' ';
+ if ($admin) $o .= '
X ';
+ $o .= '
';
+ }
+ return $o.'
';
+ }
+}
+
+class ContactManager {
+ private $file = 'feedbacks.json';
+ public function displayForm() { return '
Name: E-Mail: Nachricht: Senden
'; }
+ public function handleSubmission($n,$e,$m) {
+ if (!$n||!$e||!$m) return ['success'=>false,'message'=>'Alle Felder ausfüllen'];
+ $fb = ['name'=>htmlspecialchars($n),'email'=>filter_var($e,FILTER_SANITIZE_EMAIL),'message'=>htmlspecialchars($m),'date'=>date('Y-m-d H:i:s'),'ip'=>$_SERVER['REMOTE_ADDR']??''];
+ $all = file_exists($this->file) ? json_decode(file_get_contents($this->file),true) : [];
+ $all[] = $fb;
+ file_put_contents($this->file, json_encode($all, JSON_PRETTY_PRINT));
+ return ['success'=>true,'message'=>'Nachricht gesendet!'];
+ }
+ public function deleteFeedback($i) { $all = json_decode(file_get_contents($this->file),true); if (isset($all[$i])) { unset($all[$i]); file_put_contents($this->file, json_encode(array_values($all),JSON_PRETTY_PRINT)); return true; } return false; }
+}
+
+class AdminManager {
+ public function isAdmin() { return isset($_SESSION['admin']) && $_SESSION['admin'] === true; }
+ public function handleLogin($u,$p) { if ($u==='admin' && $p==='sonne4000$$$$Q') { $_SESSION['admin']=true; return true; } return false; }
+ public function displayLoginForm() { return '
User: Pass: Login '; }
+ public function displayAdminContent() {
+ global $settingsManager;
+ $o = '
';
+ $o .= '
📩 Nachrichten ';
+ $msgs = file_exists('feedbacks.json') ? json_decode(file_get_contents('feedbacks.json'),true) : [];
+ foreach ($msgs as $i=>$m) {
+ $o .= '
'.$m['name'].' ('.$m['email'].')
'.$m['message'].'
'.$m['date'].' ';
+ $o .= '
X ';
+ }
+ if (!$msgs) $o .= '
Keine Nachrichten.
';
+ $o .= '
';
+ return $o;
+ }
+ public function displayGalleryImages() {
+ $o = '
';
+ foreach (glob("uploads/*.{jpg,jpeg,png,gif}",GLOB_BRACE) as $f) $o .= '
';
+ return $o.'
';
+ }
+}
+
+class VideoArchiveManager {
+ private $dir;
+ public function __construct($d='./videos/') { $this->dir = $d; }
+ public function handleSpecificVideoDownload() {
+ if (isset($_GET['download_specific_video'],$_GET['token'])) {
+ $p = $_GET['download_specific_video'];
+ if (!hash_equals(hash_hmac('sha256',$p,session_id()), $_GET['token'])) { echo "Invalid"; exit; }
+ $rp = realpath($p);
+ $rd = realpath($this->dir);
+ if ($rp && strpos($rp,$rd)===0 && file_exists($rp)) {
+ header('Content-Type: video/mp4');
+ header('Content-Disposition: attachment; filename="'.basename($rp).'"');
+ header('Content-Length: '.filesize($rp));
+ readfile($rp);
+ exit;
+ }
+ echo "Not found"; exit;
+ }
+ }
+}
+
+$webcamManager = new WebcamManager();
+$guestbookManager = new GuestbookManager();
+$contactManager = new ContactManager();
+$adminManager = new AdminManager();
+$videoArchiveManager = new VideoArchiveManager('./videos/');
+$videoArchiveManager->handleSpecificVideoDownload();
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ if (isset($_POST['action']) && $_POST['action'] === 'viewer_heartbeat') $viewerCounter->handleHeartbeat();
+ if (isset($_POST['guestbook'])) { $guestbookManager->handleFormSubmission(); header("Location: ".$_SERVER['PHP_SELF']."#guestbook"); exit; }
+ if (isset($_POST['contact'])) {
+ $r = $contactManager->handleSubmission($_POST['name'],$_POST['email'],$_POST['message']);
+ if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])) { header('Content-Type: application/json'); echo json_encode($r); exit; }
+ header('Location: '.$_SERVER['PHP_SELF'].'#kontakt'); exit;
+ }
+ if (isset($_POST['admin-login'])) { $adminManager->handleLogin($_POST['username'],$_POST['password']); header('Location: '.$_SERVER['PHP_SELF'].'#admin'); exit; }
+ if ($adminManager->isAdmin()) {
+ if (isset($_POST['action']) && $_POST['action']==='delete_guestbook') { $guestbookManager->deleteEntry(intval($_POST['delete_entry'])); header("Location: ".$_SERVER['PHP_SELF']."#guestbook"); exit; }
+ if (isset($_POST['action']) && $_POST['action']==='delete_feedback') { $contactManager->deleteFeedback(intval($_POST['delete_index'])); header("Location: ".$_SERVER['PHP_SELF']."#admin"); exit; }
+ }
+}
+
+$vc = $viewerCounter->getInitialCount();
+$sv = $settingsManager->get('viewer_display.enabled') && $vc >= $settingsManager->get('viewer_display.min_viewers');
+$mv = $settingsManager->get('viewer_display.min_viewers');
+?>
+
+
+
+
+
Aurora Livecam
+
+
+
+
+
+
+
+
+
+Aurora Wetter Livecam
+Faszinierende Ausblicke aus dem Zürcher Oberland
+
+
+
+
+
+
+displayWebcam(); ?>
+
+
+
+−
+⟲
++
+
+
+
+
+
+
+
+
+
+--:--:--
+1x
+ Live
+
+
+
+
Zurück zu Live
+
+
+displayStreamStats(); ?>
+
Zuschauer
+
+
+
+
+
+
+
+
+
📅 Videoarchiv
+displayVisualCalendar(); ?>
+
+
+
+
+
+
Gästebuch
+displayForm(); echo $guestbookManager->displayEntries($adminManager->isAdmin()); ?>
+
+
+
+
+
+
Kontakt
+displayForm(); ?>
+
+
+
+
+
+
Galerie
+displayGalleryImages(); ?>
+
+
+
+isAdmin()): ?>
+
+
+
⚙️ Admin
+displayAdminContent(); ?>
+
+
+
+
+
+
Admin Login
+displayLoginForm(); ?>
+
+
+
+
+
+
+
+
×
+
+
+
+
+
+
diff --git a/aurora-livecam/js/video-zoom.js b/aurora-livecam/js/video-zoom.js
new file mode 100644
index 0000000..3c7ac4b
--- /dev/null
+++ b/aurora-livecam/js/video-zoom.js
@@ -0,0 +1,55 @@
+/**
+ * Video Zoom Controller - Zoom für alle Video-Modi
+ */
+let currentZoom = 1;
+
+function applyZoom(zoomValue) {
+ const config = window.zoomConfig || { minZoom: 1, maxZoom: 4 };
+ currentZoom = Math.max(config.minZoom, Math.min(config.maxZoom, zoomValue));
+
+ const valueEl = document.getElementById('zoom-value');
+ const slider = document.getElementById('zoom-range');
+
+ if (valueEl) valueEl.textContent = currentZoom + 'x';
+ if (slider) slider.value = currentZoom;
+
+ // Alle Video-Elemente zoomen
+ const targets = [
+ document.getElementById('webcam-player'),
+ document.getElementById('timelapse-image'),
+ document.getElementById('daily-video')
+ ].filter(Boolean);
+
+ targets.forEach((el) => {
+ el.style.transform = `scale(${currentZoom})`;
+ el.style.transformOrigin = 'center center';
+ el.style.transition = 'transform 0.2s ease';
+ });
+}
+
+function adjustZoom(delta) {
+ applyZoom(currentZoom + delta);
+}
+
+function resetZoom() {
+ applyZoom(1);
+}
+
+// Initialisierung
+document.addEventListener('DOMContentLoaded', function() {
+ const config = window.zoomConfig || {};
+ if (!config.enabled) return;
+
+ const slider = document.getElementById('zoom-range');
+ if (slider) {
+ slider.addEventListener('input', (event) => {
+ applyZoom(Number(event.target.value));
+ });
+ }
+
+ // Initial zoom anwenden (ohne transform bei 1x)
+ currentZoom = config.defaultZoom || 1;
+ if (currentZoom !== 1) {
+ applyZoom(currentZoom);
+ }
+});