getClientOriginalExtension()); if ($extension === 'zip') { return $this->importZip($file); } if ($extension === 'pro') { return [$this->importProFile($file->getRealPath())]; } throw new \InvalidArgumentException('Nur .pro und .zip Dateien sind erlaubt.'); } /** @return Song[] */ private function importZip(UploadedFile $file): array { $zip = new ZipArchive; if ($zip->open($file->getRealPath()) !== true) { throw new \RuntimeException('ZIP-Datei konnte nicht geƶffnet werden.'); } $tempDir = sys_get_temp_dir().'/pro-import-'.uniqid(); mkdir($tempDir, 0755, true); $songs = []; try { $zip->extractTo($tempDir); $zip->close(); $proFiles = glob($tempDir.'/*.pro') ?: []; $proFilesNested = glob($tempDir.'/**/*.pro') ?: []; $allProFiles = array_unique(array_merge($proFiles, $proFilesNested)); if (empty($allProFiles)) { throw new \RuntimeException('Keine .pro Dateien im ZIP-Archiv gefunden.'); } foreach ($allProFiles as $proPath) { $songs[] = $this->importProFile($proPath); } } finally { $this->deleteDirectory($tempDir); } return $songs; } private function importProFile(string $filePath): Song { $proSong = ProFileReader::read($filePath); return DB::transaction(function () use ($proSong) { return $this->upsertSong($proSong); }); } private function upsertSong(ProSong $proSong): Song { $ccliId = $proSong->getCcliSongNumber(); $songData = [ 'title' => $proSong->getName(), 'author' => $proSong->getCcliAuthor() ?: null, 'copyright_text' => $proSong->getCcliPublisher() ?: null, 'copyright_year' => $proSong->getCcliCopyrightYear() ?: null, 'publisher' => $proSong->getCcliPublisher() ?: null, ]; if ($ccliId) { $song = Song::withTrashed()->where('ccli_id', (string) $ccliId)->first(); if ($song) { if ($song->trashed()) { $song->restore(); } $song->update($songData); } else { $song = Song::create(array_merge($songData, ['ccli_id' => (string) $ccliId])); } } else { $song = Song::create(array_merge($songData, ['ccli_id' => null])); } $song->arrangements()->each(function (SongArrangement $arr) { $arr->arrangementGroups()->delete(); }); $song->arrangements()->delete(); $song->groups()->each(function (SongGroup $group) { $group->slides()->delete(); }); $song->groups()->delete(); $hasTranslation = false; $groupMap = []; foreach ($proSong->getGroups() as $position => $proGroup) { $color = $proGroup->getColor(); $hexColor = $color ? self::rgbaToHex($color) : '#808080'; $songGroup = $song->groups()->create([ 'name' => $proGroup->getName(), 'color' => $hexColor, 'order' => $position, ]); $groupMap[$proGroup->getName()] = $songGroup; foreach ($proSong->getSlidesForGroup($proGroup) as $slidePosition => $proSlide) { $translatedText = null; if ($proSlide->hasTranslation()) { $translatedText = $proSlide->getTranslation()->getPlainText(); $hasTranslation = true; } $songGroup->slides()->create([ 'order' => $slidePosition, 'text_content' => $proSlide->getPlainText(), 'text_content_translated' => $translatedText, ]); } } $song->update(['has_translation' => $hasTranslation]); foreach ($proSong->getArrangements() as $proArrangement) { $arrangement = $song->arrangements()->create([ 'name' => $proArrangement->getName(), 'is_default' => strtolower($proArrangement->getName()) === 'normal', ]); $groupsInArrangement = $proSong->getGroupsForArrangement($proArrangement); foreach ($groupsInArrangement as $order => $proGroup) { $songGroup = $groupMap[$proGroup->getName()] ?? null; if ($songGroup) { SongArrangementGroup::create([ 'song_arrangement_id' => $arrangement->id, 'song_group_id' => $songGroup->id, 'order' => $order, ]); } } } return $song->fresh(['groups.slides', 'arrangements.arrangementGroups']); } public static function rgbaToHex(array $rgba): string { $r = (int) round(($rgba['r'] ?? 0) * 255); $g = (int) round(($rgba['g'] ?? 0) * 255); $b = (int) round(($rgba['b'] ?? 0) * 255); return sprintf('#%02X%02X%02X', $r, $g, $b); } public static function hexToRgba(string $hex): array { $hex = ltrim($hex, '#'); $r = hexdec(substr($hex, 0, 2)) / 255; $g = hexdec(substr($hex, 2, 2)) / 255; $b = hexdec(substr($hex, 4, 2)) / 255; return [round($r, 4), round($g, 4), round($b, 4), 1.0]; } private function deleteDirectory(string $dir): void { if (! is_dir($dir)) { return; } $items = scandir($dir); foreach ($items as $item) { if ($item === '.' || $item === '..') { continue; } $path = $dir.'/'.$item; is_dir($path) ? $this->deleteDirectory($path) : unlink($path); } rmdir($dir); } }