sync.json (einmalig) $legacyFile = __DIR__ . DIRECTORY_SEPARATOR . 'sync.txt'; if (!file_exists(STATE_FILE) && file_exists($legacyFile)) { @rename($legacyFile, STATE_FILE); } // Songs-Ordner sicherstellen if (!is_dir(SONGS_DIR)) { @mkdir(SONGS_DIR, 0775, true); } // ----- Pfad-Helper ----- function norm_path($p) { $r = realpath($p); if ($r === false) return false; $r = str_replace('\\', '/', $r); if (DIRECTORY_SEPARATOR === '\\') $r = strtolower($r); return rtrim($r, '/'); } function is_path_inside($base, $path) { $b = norm_path($base); $p = norm_path($path); if ($b === false || $p === false) return false; return strpos($p, $b) === 0; } // ----- Datei lesen & UTF-8 ----- function read_file_utf8($path) { $bin = @file_get_contents($path); if ($bin === false) return ''; $bin = preg_replace("/^\xEF\xBB\xBF/", '', $bin); return $bin; } // ----- .sng Parser ----- function parse_sng($path) { $raw = read_file_utf8($path); $lines = preg_split("/(\r\n|\r|\n)/", $raw); $meta = []; $sections = []; $current = null; $nonMetaAll = []; foreach ($lines as $ln) { $r = rtrim($ln, "\r\n"); if (preg_match('/^#(\w+)=(.*)$/', $r, $m)) { $meta[$m[1]] = $m[2]; continue; } if (preg_match('/^\s*\[(.+?)\]\s*$/u', $r, $m)) { if ($current) $sections[] = $current; $current = ['name' => $m[1], 'text' => '']; continue; } $nonMetaAll[] = $r; if ($current) $current['text'] .= ($current['text'] === '' ? '' : "\n") . $r; } if ($current) $sections[] = $current; if (!$sections) { $body = trim(implode("\n", $nonMetaAll)); $sections = [['name' => $meta['Title'] ?? basename($path, '.sng'), 'text' => $body]]; } foreach ($sections as &$s) { $normalized = str_replace("\r", '', $s['text']); $slides = preg_split('/\n\s*\n|^\s*---+\s*$/m', trim($normalized)); $s['slides'] = array_values(array_filter(array_map('trim', $slides), fn($x) => $x !== '')); } return ['meta' => $meta, 'sections' => $sections]; } // ----- JSON-Ausgabe ----- function json_out($data) { header('Content-Type: application/json; charset=UTF-8'); echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } // ==================== API Router ==================== $action = $_GET['action'] ?? null; if ($action === 'song') { $rel = $_GET['file'] ?? ''; $rel = ltrim(str_replace(['..', '\\'], ['', '/'], $rel), '/'); $abs = SONGS_DIR . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $rel); $absReal = realpath($abs); if (!$absReal || !is_path_inside(SONGS_DIR, $absReal) || !is_file($absReal) || strtolower(pathinfo($absReal, PATHINFO_EXTENSION)) !== 'sng') { http_response_code(404); json_out(['ok' => false, 'error' => 'Song not found']); } $parsed = parse_sng($absReal); $info = [ 'title' => $parsed['meta']['Title'] ?? pathinfo($absReal, PATHINFO_FILENAME), 'langcount' => (int)($parsed['meta']['LangCount'] ?? 1), 'author' => $parsed['meta']['Author'] ?? ($parsed['meta']['Writer'] ?? ''), 'copyright' => $parsed['meta']['Copyright'] ?? '', 'churchsongid' => $parsed['meta']['ChurchSongID'] ?? '' ]; json_out(['ok' => true, 'info' => $info, 'sections' => $parsed['sections']]); } if ($action === 'get_state') { if (file_exists(STATE_FILE)) { $data = json_decode(file_get_contents(STATE_FILE), true); json_out(['ok' => true, 'state' => $data]); } else { json_out(['ok' => true, 'state' => null]); } } ?> SongBeamer Beamer
Keine Auswahl