title, $this->buildGroups($song, $service), $this->buildArrangements($song), $this->buildCcliMetadata($song), ); return $tempPath; } public function generateParserSong(Song $song, ?Service $service = null): \ProPresenter\Parser\Song { $song->loadMissing(['arrangements.arrangementLabels.label.songSlides']); return ProFileGenerator::generate( $song->title, $this->buildGroups($song, $service), $this->buildArrangements($song), $this->buildCcliMetadata($song), ); } private function buildGroups(Song $song, ?Service $service = null): array { $defaultArr = $song->arrangements->firstWhere('is_default', true) ?? $song->arrangements->first(); if ($defaultArr === null) { return []; } $defaultArr->loadMissing('arrangementLabels.label.songSlides'); $groups = []; $seenLabelIds = []; foreach ($defaultArr->arrangementLabels->sortBy('order') as $arrangementLabel) { $label = $arrangementLabel->label; if ($label === null) { continue; } if (in_array($label->id, $seenLabelIds, true)) { continue; } $seenLabelIds[] = $label->id; $slides = []; $labelSlides = $label->songSlides->sortBy('order')->values(); $totalSlides = $labelSlides->count(); foreach ($labelSlides as $slideIndex => $slide) { $slideData = ['text' => $slide->text_content ?? '']; if ($slide->text_content_translated) { $slideData['translation'] = $slide->text_content_translated; } if ($service !== null) { $macros = $this->macroResolutionService->macrosForSlide( $service, 'song', ['index' => $slideIndex, 'total' => $totalSlides, 'label_id' => $label->id], ); if (! empty($macros)) { // ProPresenter parser currently supports one `macro` entry per slide; keep the first resolved macro until stacked macros are supported. $slideData['macro'] = $macros[0]; } } $slides[] = $slideData; } $groups[] = [ 'name' => $label->name, 'color' => ProImportService::hexToRgba($label->color ?? '#808080'), 'slides' => $slides, ]; } return $groups; } private function buildArrangements(Song $song): array { $arrangements = []; foreach ($song->arrangements as $arrangement) { $arrangement->loadMissing('arrangementLabels.label'); $groupNames = $arrangement->arrangementLabels ->sortBy('order') ->map(fn ($al) => $al->label?->name) ->filter() ->values() ->toArray(); $arrangements[] = [ 'name' => $arrangement->name, 'groupNames' => $groupNames, ]; } return $arrangements; } private function buildCcliMetadata(Song $song): array { return array_filter([ 'author' => $song->author, 'song_title' => $song->title, 'copyright_year' => $song->copyright_year, 'publisher' => $song->publisher, 'song_number' => $song->ccli_id ? (int) $song->ccli_id : null, ]); } }