From 062cbfe307c8e75ceeedd7b42e3e9ca9deb67566 Mon Sep 17 00:00:00 2001 From: Metacube Date: Sun, 5 Apr 2026 09:00:43 +0200 Subject: [PATCH] Delete indexnew.php --- indexnew.php | 1720 -------------------------------------------------- 1 file changed, 1720 deletions(-) delete mode 100644 indexnew.php diff --git a/indexnew.php b/indexnew.php deleted file mode 100644 index a1de8eb..0000000 --- a/indexnew.php +++ /dev/null @@ -1,1720 +0,0 @@ - [ - 'title' => 'Aurora Weather Livecam', - 'welcome' => 'Willkommen bei der Aurora Live Webcam', - 'subline' => 'Livebilder, Zeitraffer, Archiv & Community – alles in einem sonnigen Dashboard.', - 'live' => 'Live', - 'timelapse' => 'Zeitraffer', - 'archive' => 'Archiv', - 'gallery' => 'Galerie', - 'community' => 'Community', - 'contact' => 'Kontakt', - 'guestbook' => 'Gästebuch', - 'send' => 'Senden', - 'name' => 'Name', - 'email' => 'E-Mail', - 'message' => 'Nachricht', - 'screenshot' => 'Screenshot', - 'clip' => 'Clip aufnehmen', - 'download' => 'Neueste Aufnahme laden', - 'calendar_title' => 'Visueller Wetterkalender', - 'language' => 'Sprache', - 'rating' => 'Bewertung', - 'comment' => 'Kommentar', - 'add_entry' => 'Eintrag hinzufügen', - 'view_all' => 'Alle anzeigen', - 'privacy' => 'Privatsphäre', - 'share' => 'Teilen', - 'pip' => 'Bild-in-Bild', - 'stats' => 'Stream-Status', - 'starlink' => 'Starlink-Verbindung', - 'starlink_caption' => 'Scanne den QR-Code für das Starlink Satelliteninternet.', - 'starlink_alt' => 'Starlink QR-Code', - 'timelapse_title' => 'Zeitraffer-Magie', - 'timelapse_caption' => 'Die letzten Keyframes werden automatisch abgespielt.', - 'archive_title' => 'Video-Archiv', - 'archive_hint' => 'Wähle Jahr und Monat um gespeicherte Clips zu laden.', - 'no_videos' => 'Keine Videos verfügbar.', - 'download_video' => 'Video herunterladen', - ], - 'en' => [ - 'title' => 'Aurora Weather Livecam', - 'welcome' => 'Welcome to the Aurora Live Webcam', - 'subline' => 'Live footage, timelapse, archive & community – all in one sunny dashboard.', - 'live' => 'Live', - 'timelapse' => 'Timelapse', - 'archive' => 'Archive', - 'gallery' => 'Gallery', - 'community' => 'Community', - 'contact' => 'Contact', - 'guestbook' => 'Guestbook', - 'send' => 'Send', - 'name' => 'Name', - 'email' => 'Email', - 'message' => 'Message', - 'screenshot' => 'Screenshot', - 'clip' => 'Record clip', - 'download' => 'Download latest capture', - 'calendar_title' => 'Visual Weather Calendar', - 'language' => 'Language', - 'rating' => 'Rating', - 'comment' => 'Comment', - 'add_entry' => 'Add entry', - 'view_all' => 'View all', - 'privacy' => 'Privacy', - 'share' => 'Share', - 'pip' => 'Picture-in-Picture', - 'stats' => 'Stream status', - 'starlink' => 'Starlink Connection', - 'starlink_caption' => 'Scan the QR code to reach Starlink satellite internet.', - 'starlink_alt' => 'Starlink QR code', - 'timelapse_title' => 'Timelapse magic', - 'timelapse_caption' => 'Latest keyframes will autoplay.', - 'archive_title' => 'Video archive', - 'archive_hint' => 'Pick a year and month to browse stored clips.', - 'no_videos' => 'No videos available.', - 'download_video' => 'Download clip', - ], - 'it' => [ - 'title' => 'Aurora Weather Livecam', - 'welcome' => 'Benvenuto alla webcam Aurora', - 'subline' => 'Immagini live, time-lapse, archivio e community – tutto in un unico cruscotto soleggiato.', - 'live' => 'Live', - 'timelapse' => 'Time-lapse', - 'archive' => 'Archivio', - 'gallery' => 'Galleria', - 'community' => 'Community', - 'contact' => 'Contatto', - 'guestbook' => 'Guestbook', - 'send' => 'Invia', - 'name' => 'Nome', - 'email' => 'Email', - 'message' => 'Messaggio', - 'screenshot' => 'Screenshot', - 'clip' => 'Registra clip', - 'download' => 'Scarica ultima cattura', - 'calendar_title' => 'Calendario Meteo Visivo', - 'language' => 'Lingua', - 'rating' => 'Valutazione', - 'comment' => 'Commento', - 'add_entry' => 'Aggiungi voce', - 'view_all' => 'Vedi tutto', - 'privacy' => 'Privacy', - 'share' => 'Condividi', - 'pip' => 'Picture-in-Picture', - 'stats' => 'Stato stream', - 'starlink' => 'Connessione Starlink', - 'starlink_caption' => 'Scansiona il QR code per internet satellitare Starlink.', - 'starlink_alt' => 'QR code Starlink', - 'timelapse_title' => 'Magia time-lapse', - 'timelapse_caption' => 'Gli ultimi keyframe vengono riprodotti automaticamente.', - 'archive_title' => 'Archivio video', - 'archive_hint' => 'Scegli anno e mese per sfogliare le clip.', - 'no_videos' => 'Nessun video disponibile.', - 'download_video' => 'Scarica clip', - ], - 'fr' => [ - 'title' => 'Aurora Weather Livecam', - 'welcome' => 'Bienvenue sur la webcam Aurora', - 'subline' => 'Images en direct, time-lapse, archives et communauté – dans un tableau de bord ensoleillé.', - 'live' => 'Direct', - 'timelapse' => 'Time-lapse', - 'archive' => 'Archive', - 'gallery' => 'Galerie', - 'community' => 'Communauté', - 'contact' => 'Contact', - 'guestbook' => 'Livre d’or', - 'send' => 'Envoyer', - 'name' => 'Nom', - 'email' => 'E-mail', - 'message' => 'Message', - 'screenshot' => 'Capture', - 'clip' => 'Enregistrer un clip', - 'download' => 'Télécharger la dernière capture', - 'calendar_title' => 'Calendrier météo visuel', - 'language' => 'Langue', - 'rating' => 'Évaluation', - 'comment' => 'Commentaire', - 'add_entry' => 'Ajouter une entrée', - 'view_all' => 'Tout voir', - 'privacy' => 'Confidentialité', - 'share' => 'Partager', - 'pip' => 'Image dans l’image', - 'stats' => 'Statut du flux', - 'starlink' => 'Connexion Starlink', - 'starlink_caption' => 'Scannez le QR code pour internet par satellite Starlink.', - 'starlink_alt' => 'QR code Starlink', - 'timelapse_title' => 'Magie time-lapse', - 'timelapse_caption' => 'Les derniers keyframes se jouent automatiquement.', - 'archive_title' => 'Archive vidéo', - 'archive_hint' => 'Choisissez année et mois pour parcourir les clips.', - 'no_videos' => 'Aucune vidéo disponible.', - 'download_video' => 'Télécharger la vidéo', - ], - 'zh' => [ - 'title' => '极光天气直播摄像头', - 'welcome' => '欢迎来到极光直播摄像头', - 'subline' => '实时画面、延时摄影、档案与社区——尽在阳光仪表盘。', - 'live' => '直播', - 'timelapse' => '延时摄影', - 'archive' => '档案', - 'gallery' => '图集', - 'community' => '社区', - 'contact' => '联系', - 'guestbook' => '留言簿', - 'send' => '发送', - 'name' => '姓名', - 'email' => '邮箱', - 'message' => '留言', - 'screenshot' => '截图', - 'clip' => '录制剪辑', - 'download' => '下载最新捕获', - 'calendar_title' => '可视化天气日历', - 'language' => '语言', - 'rating' => '评分', - 'comment' => '评论', - 'add_entry' => '添加条目', - 'view_all' => '查看全部', - 'privacy' => '隐私', - 'share' => '分享', - 'pip' => '画中画', - 'stats' => '流状态', - 'starlink' => 'Starlink 连接', - 'starlink_caption' => '扫描二维码访问 Starlink 卫星网络。', - 'starlink_alt' => 'Starlink 二维码', - 'timelapse_title' => '延时魔法', - 'timelapse_caption' => '自动播放最新关键帧。', - 'archive_title' => '视频档案', - 'archive_hint' => '选择年份和月份来浏览存档剪辑。', - 'no_videos' => '暂无可用视频。', - 'download_video' => '下载视频', - ], - ]; - - public function getCurrentLocale(): string - { - if (isset($_POST['language'])) { - $_SESSION['lang'] = $_POST['language']; - } - return $_SESSION['lang'] ?? 'de'; - } - - public function get(string $key, ?string $locale = null): string - { - $locale = $locale ?? $this->getCurrentLocale(); - $locale = array_key_exists($locale, $this->translations) ? $locale : 'de'; - return $this->translations[$locale][$key] ?? $this->translations['de'][$key] ?? $key; - } - - public function getAllTranslations(): array - { - return $this->translations; - } -} - -class WebcamManager -{ - private string $videoSrc; - - public function __construct(string $videoSrc = STREAM_SOURCE) - { - $this->videoSrc = $videoSrc; - } - - public function getVideoSrc(): string - { - return $this->videoSrc; - } - - public function getImageFiles(): array - { - $files = glob(IMAGE_DIR . '/screenshot_*.jpg') ?: []; - usort($files, static fn(string $a, string $b) => filemtime($b) <=> filemtime($a)); - return array_slice($files, 0, 10); - } - - public function getLatestVideo(): ?string - { - $videos = glob(VIDEO_DIR . '/*.mp4'); - if (!$videos) { - return null; - } - usort($videos, static fn(string $a, string $b) => filemtime($b) <=> filemtime($a)); - return $videos[0]; - } - - public function captureSnapshot(): array - { - $outputFile = 'snapshot_' . date('YmdHis') . '.jpg'; - $targetPath = UPLOAD_DIR . '/' . $outputFile; - $command = sprintf( - "ffmpeg -y -i %s -i %s -filter_complex 'overlay=main_w-overlay_w-10:10' -frames:v 1 -q:v 2 %s", - escapeshellarg($this->videoSrc), - escapeshellarg(LOGO_PATH), - escapeshellarg($targetPath) - ); - exec($command, $output, $returnVar); - if ($returnVar !== 0 || !file_exists($targetPath)) { - return ['success' => false, 'message' => 'Snapshot konnte nicht erstellt werden.']; - } - return ['success' => true, 'file' => basename($targetPath)]; - } - - public function captureClip(int $duration = 10): array - { - $outputFile = 'sequence_' . date('YmdHis') . '.mp4'; - $targetPath = UPLOAD_DIR . '/' . $outputFile; - $command = sprintf( - "ffmpeg -y -i %s -i %s -filter_complex 'overlay=10:10' -t %d -c:v libx264 -preset fast -crf 23 %s", - escapeshellarg($this->videoSrc), - escapeshellarg(LOGO_PATH), - $duration, - escapeshellarg($targetPath) - ); - exec($command, $output, $returnVar); - if ($returnVar !== 0 || !file_exists($targetPath)) { - return ['success' => false, 'message' => 'Clip konnte nicht erstellt werden.']; - } - return ['success' => true, 'file' => basename($targetPath)]; - } - - public function getStreamStats(): array - { - return [ - 'bitrate' => rand(4200, 6200), - 'latency' => rand(3, 9), - 'updated' => date('H:i:s'), - ]; - } - - public function getGallery(): array - { - $images = []; - foreach (glob(GALLERY_DIR . '/*.{jpg,jpeg,png,gif}', GLOB_BRACE) ?: [] as $file) { - $timestamp = filemtime($file) ?: 0; - $images[] = [ - 'src' => str_replace(__DIR__ . '/', '', $file), - 'date' => date('Y-m-d H:i', $timestamp), - 'timestamp' => $timestamp, - ]; - } - usort($images, static fn(array $a, array $b) => $b['timestamp'] <=> $a['timestamp']); - $images = array_slice($images, 0, 10); - return array_map(static fn(array $image) => [ - 'src' => $image['src'], - 'date' => $image['date'], - ], $images); - } -} - -class VisualCalendarManager -{ - private string $videoDir; - private array $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 __construct(string $videoDir = VIDEO_DIR) - { - $this->videoDir = rtrim($videoDir, '/'); - } - - public function getMonthData(int $year, int $month): array - { - $firstDay = new DateTimeImmutable(sprintf('%04d-%02d-01', $year, $month)); - $daysInMonth = (int) $firstDay->format('t'); - $days = []; - for ($day = 1; $day <= $daysInMonth; $day++) { - $date = sprintf('%04d%02d%02d', $year, $month, $day); - $pattern = $this->videoDir . "/daily_video_{$date}_*.mp4"; - $matches = glob($pattern) ?: []; - $days[] = [ - 'day' => $day, - 'hasVideos' => !empty($matches), - 'count' => count($matches) - ]; - } - return [ - 'year' => $year, - 'month' => $month, - 'monthName' => $this->monthNames[$month] ?? $this->monthNames[date('n')], - 'days' => $days, - ]; - } - - public function getVideosForDate(int $year, int $month, int $day): array - { - $date = sprintf('%04d%02d%02d', $year, $month, $day); - $videos = []; - foreach (glob($this->videoDir . "/daily_video_{$date}_*.mp4") as $file) { - $fileName = basename($file); - $token = generate_download_token($fileName); - $videos[] = [ - 'file' => $fileName, - 'size' => filesize($file), - 'time' => date('H:i', filemtime($file)), - 'sizeFormatted' => round(filesize($file) / (1024 * 1024), 2), - 'token' => $token, - 'download' => '?download_specific_video=' . rawurlencode($fileName) . '&token=' . $token, - 'day' => $day, - ]; - } - return $videos; - } -} - -class VideoArchiveManager -{ - private string $videoDir; - private array $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 __construct(string $videoDir = VIDEO_DIR) - { - $this->videoDir = rtrim($videoDir, '/'); - } - - public function getVideosGroupedByDate(): array - { - $videos = []; - foreach (glob($this->videoDir . '/daily_video_*.mp4') ?: [] as $video) { - if (!preg_match('/daily_video_(\d{8})_\d{6}\.mp4$/', basename($video), $matches)) { - continue; - } - $dateStr = $matches[1]; - $year = substr($dateStr, 0, 4); - $month = substr($dateStr, 4, 2); - $videos[$year][$month][] = $this->formatVideoRecord($video); - } - foreach ($videos as $year => $months) { - foreach ($months as $month => $items) { - usort($videos[$year][$month], static fn(array $a, array $b) => $b['timestamp'] <=> $a['timestamp']); - } - } - krsort($videos); - return $videos; - } - - public function getAvailableYearsAndMonths(): array - { - $grouped = $this->getVideosGroupedByDate(); - $result = []; - foreach ($grouped as $year => $months) { - $result[$year] = array_values(array_keys($months)); - rsort($result[$year]); - } - return $result; - } - - public function getVideosForYearAndMonth(int $year, string $month): array - { - $month = str_pad($month, 2, '0', STR_PAD_LEFT); - $grouped = $this->getVideosGroupedByDate(); - return $grouped[$year][$month] ?? []; - } - - public function getMonthName(string $month): string - { - $month = str_pad($month, 2, '0', STR_PAD_LEFT); - return $this->monthNames[$month] ?? $month; - } - - public function handleDownloadRequest(?string $fileName, ?string $token): void - { - if ($fileName === null || $token === null) { - return; - } - $cleanFile = basename($fileName); - $expected = generate_download_token($cleanFile); - if (!hash_equals($expected, $token)) { - respond_json(['message' => 'Ungültiger Download-Token.'], 403); - } - $videoDir = realpath($this->videoDir); - $fullPath = realpath($this->videoDir . '/' . $cleanFile); - if (!$videoDir || !$fullPath || strpos($fullPath, $videoDir) !== 0 || !file_exists($fullPath)) { - respond_json(['message' => 'Datei nicht gefunden.'], 404); - } - header('Content-Description: File Transfer'); - header('Content-Type: video/mp4'); - header('Content-Disposition: attachment; filename="' . basename($fullPath) . '"'); - header('Content-Length: ' . filesize($fullPath)); - readfile($fullPath); - exit; - } - - private function formatVideoRecord(string $path): array - { - $fileName = basename($path); - $timestamp = filemtime($path) ?: time(); - $size = filesize($path) ?: 0; - $date = DateTimeImmutable::createFromFormat('YmdHis', preg_replace('/[^0-9]/', '', $fileName)) ?: new DateTimeImmutable('@' . $timestamp); - $token = generate_download_token($fileName); - return [ - 'file' => $fileName, - 'path' => $path, - 'day' => (int) $date->format('d'), - 'time' => $date->format('H:i'), - 'size' => $size, - 'sizeFormatted' => round($size / (1024 * 1024), 2), - 'timestamp' => $timestamp, - 'download' => '?download_specific_video=' . rawurlencode($fileName) . '&token=' . $token, - 'token' => $token, - ]; - } -} - -class GuestbookManager -{ - private array $entries = []; - - public function __construct() - { - $content = json_decode((string) file_get_contents(GUESTBOOK_FILE), true); - $this->entries = is_array($content) ? $content : []; - } - - private function persist(): void - { - file_put_contents(GUESTBOOK_FILE, json_encode($this->entries, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); - } - - public function addEntry(string $name, string $message, int $rating = 5): array - { - $entry = [ - 'name' => htmlspecialchars($name, ENT_QUOTES, 'UTF-8'), - 'message' => htmlspecialchars($message, ENT_QUOTES, 'UTF-8'), - 'rating' => max(1, min(5, $rating)), - 'created' => date('Y-m-d H:i:s') - ]; - $this->entries[] = $entry; - $this->persist(); - return $entry; - } - - public function getEntries(int $limit = 10): array - { - return array_slice(array_reverse($this->entries), 0, $limit); - } - - public function deleteEntry(int $index): bool - { - if (!isset($this->entries[$index])) { - return false; - } - unset($this->entries[$index]); - $this->entries = array_values($this->entries); - $this->persist(); - return true; - } -} - -class ContactManager -{ - private string $adminEmail = 'metacube@gmail.com'; - private string $gmailUser = 'metacube@gmail.com'; - private string $gmailAppPassword = 'qggk hsxz fdkq jgxa'; - - public function handle(string $name, string $email, string $message): array - { - if ($name === '' || $email === '' || $message === '') { - return ['success' => false, 'message' => 'Bitte alle Felder ausfüllen.']; - } - if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { - return ['success' => false, 'message' => 'Bitte eine gültige E-Mail-Adresse verwenden.']; - } - if (mb_strlen($message) < 10) { - return ['success' => false, 'message' => 'Die Nachricht ist zu kurz.']; - } - - $payload = [ - 'name' => htmlspecialchars(trim($name), ENT_QUOTES, 'UTF-8'), - 'email' => filter_var(trim($email), FILTER_SANITIZE_EMAIL), - 'message' => htmlspecialchars(trim($message), ENT_QUOTES, 'UTF-8'), - 'date' => date('Y-m-d H:i:s'), - 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', - 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown', - ]; - - $existing = json_decode((string) file_get_contents(FEEDBACK_FILE), true); - $existing = is_array($existing) ? $existing : []; - $existing[] = $payload; - file_put_contents(FEEDBACK_FILE, json_encode($existing, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); - - $mail = new PHPMailer(true); - try { - $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($payload['email'], $payload['name']); - $mail->isHTML(true); - $mail->CharSet = 'UTF-8'; - $mail->Subject = 'Neue Kontaktanfrage von ' . $payload['name']; - $mail->Body = '

Aurora Kontakt

' . - '

Name: ' . $payload['name'] . '

' . - '

E-Mail: ' . $payload['email'] . '

' . - '

Nachricht:
' . nl2br($payload['message']) . '

' . - '
Gesendet am ' . $payload['date'] . ' | IP ' . $payload['ip'] . ''; - $mail->send(); - } catch (Exception $e) { - error_log('Mail error: ' . $mail->ErrorInfo); - return ['success' => false, 'message' => 'Nachricht gespeichert, E-Mail konnte nicht gesendet werden.']; - } - - return ['success' => true, 'message' => 'Vielen Dank! Wir melden uns zeitnah.']; - } -} - -class AdminManager -{ - public function isAdmin(): bool - { - return isset($_SESSION['admin']) && $_SESSION['admin'] === true; - } - - public function login(string $username, string $password): bool - { - if ($username === 'admin' && $password === 'sonne4000$$$$Q') { - $_SESSION['admin'] = true; - return true; - } - return false; - } - - public function logout(): void - { - unset($_SESSION['admin']); - } - - public function handleImageUpload(array $file): array - { - if (!$this->isAdmin()) { - return ['success' => false, 'message' => 'Nicht autorisiert.']; - } - if (!isset($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) { - return ['success' => false, 'message' => 'Keine Datei empfangen.']; - } - $allowed = ['jpg', 'jpeg', 'png', 'gif']; - $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); - if (!in_array($extension, $allowed, true)) { - return ['success' => false, 'message' => 'Nur JPG, PNG oder GIF sind erlaubt.']; - } - if ($file['size'] > 5 * 1024 * 1024) { - return ['success' => false, 'message' => 'Datei ist größer als 5 MB.']; - } - $target = UPLOAD_DIR . '/' . uniqid('admin_', true) . '.' . $extension; - if (!move_uploaded_file($file['tmp_name'], $target)) { - return ['success' => false, 'message' => 'Upload fehlgeschlagen.']; - } - return ['success' => true, 'file' => basename($target)]; - } - - public function updateSocialLink(string $platform, string $url): array - { - if (!$this->isAdmin()) { - return ['success' => false, 'message' => 'Nicht autorisiert.']; - } - $links = json_decode((string) file_get_contents(SOCIAL_LINKS_FILE), true); - $links = is_array($links) ? $links : []; - $links[$platform] = $url; - file_put_contents(SOCIAL_LINKS_FILE, json_encode($links, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); - return ['success' => true, 'links' => $links]; - } - - public function getSocialLinks(): array - { - $links = json_decode((string) file_get_contents(SOCIAL_LINKS_FILE), true); - return is_array($links) ? $links : []; - } -} - -$languageManager = new LanguageManager(); -$locale = $languageManager->getCurrentLocale(); -$webcamManager = new WebcamManager(); -$calendarManager = new VisualCalendarManager(); -$guestbookManager = new GuestbookManager(); -$contactManager = new ContactManager(); -$adminManager = new AdminManager(); -$archiveManager = new VideoArchiveManager(); - -if (isset($_GET['download_specific_video'])) { - $archiveManager->handleDownloadRequest($_GET['download_specific_video'], $_GET['token'] ?? null); -} - -if (isset($_GET['download_video']) && $_GET['download_video'] === 'latest') { - $latest = $webcamManager->getLatestVideo(); - if ($latest && file_exists($latest)) { - header('Content-Description: File Transfer'); - header('Content-Type: application/octet-stream'); - header('Content-Disposition: attachment; filename="' . basename($latest) . '"'); - header('Content-Length: ' . filesize($latest)); - readfile($latest); - exit; - } - echo 'Kein Video gefunden.'; - exit; -} - -if (isset($_GET['api'])) { - $action = $_GET['api']; - switch ($action) { - case 'images': - respond_json(['images' => array_map(static fn(string $p) => str_replace(__DIR__ . '/', '', $p), $webcamManager->getImageFiles())]); - case 'gallery': - respond_json(['gallery' => $webcamManager->getGallery()]); - case 'calendar': - $year = isset($_GET['year']) ? (int) $_GET['year'] : (int) date('Y'); - $month = isset($_GET['month']) ? (int) $_GET['month'] : (int) date('n'); - respond_json(['calendar' => $calendarManager->getMonthData($year, $month)]); - case 'calendar_videos': - $year = (int) ($_GET['year'] ?? date('Y')); - $month = (int) ($_GET['month'] ?? date('n')); - $day = (int) ($_GET['day'] ?? date('j')); - respond_json(['videos' => $calendarManager->getVideosForDate($year, $month, $day)]); - case 'archive': - $year = (int) ($_GET['year'] ?? date('Y')); - $month = str_pad((string) ($_GET['month'] ?? date('n')), 2, '0', STR_PAD_LEFT); - respond_json([ - 'available' => $archiveManager->getAvailableYearsAndMonths(), - 'monthName' => $archiveManager->getMonthName($month), - 'videos' => $archiveManager->getVideosForYearAndMonth($year, $month) - ]); - case 'guestbook': - respond_json(['entries' => $guestbookManager->getEntries(50)]); - case 'stream_stats': - respond_json(['stats' => $webcamManager->getStreamStats()]); - default: - respond_json(['message' => 'Unbekannte API-Anfrage.'], 404); - } -} - -if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $action = $_POST['action'] ?? ''; - switch ($action) { - case 'capture_snapshot': - respond_json($webcamManager->captureSnapshot()); - case 'capture_clip': - $duration = isset($_POST['duration']) ? max(5, min(120, (int) $_POST['duration'])) : 10; - respond_json($webcamManager->captureClip($duration)); - case 'guestbook_add': - $name = $_POST['name'] ?? ''; - $message = $_POST['message'] ?? ''; - $rating = isset($_POST['rating']) ? (int) $_POST['rating'] : 5; - if ($name === '' || $message === '') { - respond_json(['success' => false, 'message' => 'Name und Nachricht sind erforderlich.'], 422); - } - respond_json(['success' => true, 'entry' => $guestbookManager->addEntry($name, $message, $rating)]); - case 'guestbook_delete': - if (!$adminManager->isAdmin()) { - respond_json(['success' => false, 'message' => 'Nicht autorisiert.'], 403); - } - $index = isset($_POST['entry']) ? (int) $_POST['entry'] : -1; - respond_json(['success' => $guestbookManager->deleteEntry($index)]); - case 'contact_send': - $name = $_POST['name'] ?? ''; - $email = $_POST['email'] ?? ''; - $message = $_POST['message'] ?? ''; - respond_json($contactManager->handle($name, $email, $message)); - case 'admin_login': - $username = $_POST['username'] ?? ''; - $password = $_POST['password'] ?? ''; - respond_json(['success' => $adminManager->login($username, $password)]); - case 'admin_logout': - $adminManager->logout(); - respond_json(['success' => true]); - case 'admin_upload': - respond_json($adminManager->handleImageUpload($_FILES['file'] ?? [])); - case 'social_update': - $platform = $_POST['platform'] ?? ''; - $url = $_POST['url'] ?? ''; - respond_json($adminManager->updateSocialLink($platform, $url)); - case 'set_language': - $_SESSION['lang'] = $_POST['language'] ?? 'de'; - respond_json(['success' => true, 'language' => $_SESSION['lang']]); - default: - respond_json(['message' => 'Unbekannte Aktion.'], 400); - } -} - -$translations = $languageManager->getAllTranslations(); -?> - - - - - - <?= htmlspecialchars($languageManager->get('title', $locale)) ?> - - - - - - -
-

get('welcome', $locale)) ?>

-

get('subline', $locale)) ?>

-
- -
-
-
- -
- - - - ⬇️ get('download', $locale)) ?> - - - -
-
-
-
-

- get('stats', $locale)) ?> - -- -

-

- -- -

- -
-
-

- get('language', $locale)) ?> -

-
- $values): ?> - - -
-
-
-

get('gallery', $locale)) ?>

-
-
-
-

get('starlink', $locale)) ?>

-
- - <?= htmlspecialchars($languageManager->get('starlink_alt', $locale)) ?> - - get('starlink_caption', $locale)) ?> -
-
-
-
- -
-

get('timelapse_title', $locale)) ?>

-

get('timelapse_caption', $locale)) ?>

-
- Timelapse frame -
-
-
- -
-

get('archive_title', $locale)) ?>

-

 

-
- - -
-
-
- -
-

get('calendar_title', $locale)) ?>

-
-
-
- -
-

get('guestbook', $locale)) ?>

-
- - - - - -
-
-
- -
-

get('contact', $locale)) ?>

-
- - - - - -
-
-
-
- - - - - -