diff --git a/index.php b/index.php index 0f68ebd..3fe33f0 100644 --- a/index.php +++ b/index.php @@ -1,8 +1,11 @@ '; - - - } + public function displayWebcam() { + return ''; +} + public function captureSnapshot() { @@ -143,7 +154,7 @@ class WebcamManager { public function getImageFiles() { // Screenshots aus dem image/ Ordner holen $imageFiles = glob("image/screenshot_*.jpg"); - rsort($imageFiles); // Neueste zuerst + sort($imageFiles); // Neueste zuerst return json_encode($imageFiles); } @@ -183,34 +194,56 @@ public function getImageFiles() { unlink($outputFile); exit; } - public function getJavaScript() { +public function getJavaScript() { return " document.addEventListener('DOMContentLoaded', function () { var video = document.getElementById('webcam-player'); - video.controls = false; // Versteckt alle Controls inkl. Play/Pause - var videoSrc = '{$this->videoSrc}'; - if (Hls.isSupported()) { + // Mobile Detection + var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); + var isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent); + + // Controls NUR auf Desktop verstecken + video.controls = false; + + if (isIOS) { + // iOS native HLS + video.src = videoSrc; + video.setAttribute('playsinline', ''); + video.setAttribute('webkit-playsinline', ''); + video.muted = true; + + video.addEventListener('loadedmetadata', function() { + video.play().catch(function(e) { + console.log('iOS Autoplay blockiert'); + }); + }); + + } else if (Hls.isSupported()) { var hls = new Hls({ - // WICHTIG: Live-Stream-Einstellungen - liveSyncDurationCount: 3, // Halte 3 Segmente Abstand zum Live-Edge - liveMaxLatencyDurationCount: 10, // Max 10 Segmente hinter Live - liveDurationInfinity: true, // Stream hat kein Ende - enableWorker: true, - lowLatencyMode: false, // Stabilität vor Latenz - backBufferLength: 90, // 90 Sekunden Back-Buffer - maxBufferLength: 60, // 60 Sekunden Forward-Buffer - maxMaxBufferLength: 120, // Max 120 Sekunden - maxBufferSize: 120*1000*1000, // 120MB Buffer + // Mobile-optimierte Einstellungen + liveSyncDurationCount: isMobile ? 2 : 3, + liveMaxLatencyDurationCount: isMobile ? 5 : 10, + liveDurationInfinity: true, + enableWorker: !isMobile, + lowLatencyMode: false, + backBufferLength: isMobile ? 30 : 90, + maxBufferLength: isMobile ? 30 : 60, + maxMaxBufferLength: isMobile ? 60 : 120, + maxBufferSize: isMobile ? 60*1000*1000 : 120*1000*1000, - // Starlink-Optimierungen - manifestLoadingTimeOut: 10000, - manifestLoadingMaxRetry: 5, - levelLoadingTimeOut: 10000, - levelLoadingMaxRetry: 5, - fragLoadingTimeOut: 10000, - fragLoadingMaxRetry: 5 + // Mobile-spezifische Timeouts + manifestLoadingTimeOut: isMobile ? 20000 : 10000, + manifestLoadingMaxRetry: 8, + levelLoadingTimeOut: isMobile ? 20000 : 10000, + levelLoadingMaxRetry: 8, + fragLoadingTimeOut: isMobile ? 20000 : 10000, + fragLoadingMaxRetry: 8, + + // Qualität für Mobile + startLevel: isMobile ? 0 : -1, + abrEwmaDefaultEstimate: isMobile ? 500000 : 1000000 }); hls.loadSource(videoSrc); @@ -219,10 +252,14 @@ public function getImageFiles() { hls.on(Hls.Events.MANIFEST_PARSED, function () { console.log('Stream geladen'); - // WICHTIG: Springe zum Live-Edge minus 60 Sekunden + if (isMobile) { + video.muted = true; + } + + // Live-Position anpassen if (hls.liveSyncPosition !== null) { var targetPosition = hls.liveSyncPosition - 60; - console.log('Setze Position auf: ' + targetPosition + ' (60s vor Live)'); + console.log('Setze Position auf: ' + targetPosition); video.currentTime = targetPosition; } @@ -231,17 +268,6 @@ public function getImageFiles() { }); }); - // Überwache den Live-Sync - hls.on(Hls.Events.LEVEL_UPDATED, function(event, data) { - console.log('Stream aktualisiert, neue Segmente verfügbar'); - - // Wenn wir zu weit zurückfallen, springe vor - if (video.currentTime < hls.liveSyncPosition - 120) { - console.log('Zu weit zurück, springe näher zum Live-Edge'); - video.currentTime = hls.liveSyncPosition - 60; - } - }); - // Fehlerbehandlung hls.on(Hls.Events.ERROR, function(event, data) { if (data.fatal) { @@ -265,35 +291,11 @@ public function getImageFiles() { } }); - // Automatisches Nachladen erzwingen - setInterval(function() { - if (!video.paused && !video.seeking) { - // Prüfe ob neue Segmente verfügbar sind - if (hls.levels && hls.levels.length > 0) { - var level = hls.levels[hls.currentLevel]; - if (level && level.details && level.details.live) { - console.log('Live-Stream läuft, Edge bei: ' + level.details.edge); - - // Wenn wir am Ende sind, lade neue Segmente - var bufferEnd = 0; - if (video.buffered.length > 0) { - bufferEnd = video.buffered.end(video.buffered.length - 1); - } - - if (bufferEnd - video.currentTime < 10) { - console.log('Buffer niedrig, lade neue Segmente...'); - hls.startLoad(); - } - } - } - } - }, 5000); // Alle 5 Sekunden prüfen - } else if (video.canPlayType('application/vnd.apple.mpegurl')) { - // Safari/iOS Fallback + // Fallback für andere Browser video.src = videoSrc; + video.muted = true; video.addEventListener('loadedmetadata', function () { - // Für Safari: Setze currentTime zurück für Pseudo-DVR video.currentTime = Math.max(0, video.duration - 60); video.play(); }); @@ -307,6 +309,7 @@ public function getImageFiles() { + public function setVideoSrc($src) { $this->videoSrc = $src; } @@ -325,7 +328,7 @@ class VisualCalendarManager { private $videoDir; private $monthNames; - public function __construct($videoDir = './image/') { + public function __construct($videoDir = './videos/') { $this->videoDir = $videoDir; $this->monthNames = [ 1 => ['de' => 'Januar', 'en' => 'January', 'it' => 'Gennaio', 'fr' => 'Janvier', 'zh' => '一月'], @@ -575,56 +578,291 @@ public function displayEntries($isAdmin = false) { } + class ContactManager { + //private $adminEmail = 'ingo.kohler.zh@gmail.com'; // ← Empfänger + private $adminEmail = 'metacube@gmail.com'; // ← Empfänger + private $feedbackFile = 'feedbacks.json'; + private $gmailUser = 'metacube@gmail.com'; // ← DEINE GMAIL-ADRESSE + private $gmailAppPassword = 'qggk hsxz fdkq jgxa'; // ← APP-PASSWORT VON GMAIL + public function displayForm() { return ' -
'; + + + + + + + + + + + + '; } public function handleSubmission($name, $email, $message) { + // Validierung + 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)' + ]; + } + + // Sanitize + $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 speichern $feedback = [ 'name' => $name, 'email' => $email, 'message' => $message, - 'date' => date('Y-m-d H:i:s') + 'date' => date('Y-m-d H:i:s'), + 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', + 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown' ]; - $feedbacks = json_decode(file_get_contents('feedbacks.json') ?: '[]', true); + + $feedbacks = file_exists($this->feedbackFile) + ? json_decode(file_get_contents($this->feedbackFile), true) + : []; + + if (!is_array($feedbacks)) { + $feedbacks = []; + } + $feedbacks[] = $feedback; - file_put_contents('feedbacks.json', json_encode($feedbacks)); + file_put_contents($this->feedbackFile, json_encode($feedbacks, JSON_PRETTY_PRINT)); + + // E-MAIL SENDEN MIT GMAIL SMTP + $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 { + // DEBUG-MODUS AKTIVIEREN + $mail->SMTPDebug = 2; + $mail->Debugoutput = function($str, $level) { + error_log("PHPMailer Debug: $str"); + }; + + // SMTP Konfiguration + $mail->isSMTP(); + $mail->Host = 'smtp.gmail.com'; + $mail->SMTPAuth = true; + $mail->Username = $this->gmailUser; // metacube@gmail.com + $mail->Password = $this->gmailAppPassword; // qggk hsxz fdkq jgxa + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + $mail->Port = 587; + + + + + // Absender & Empfänger + $mail->setFrom($this->gmailUser, 'Aurora Livecam'); + $mail->addAddress($this->adminEmail); // admin@aurora-live-weathercam.com + $mail->addReplyTo($email, $name); + + // Inhalt + $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; + } + } + + private function getEmailTemplate($name, $email, $message, $date, $ip) { + return " + + + + + + + +Aurora Weather Livecam
+Klicke auf die Events, die heute eingetreten sind:
- - -+ Für hochwertige Webcam-Übertragungen empfehlen wir Starlink - + Highspeed-Internet überall verfügbar, perfekt für abgelegene Standorte! +
+ +
+
+
+
+
+