put('slides/keyvisual.jpg', 'keyvisual-image'); $service = Service::factory()->create([ 'title' => 'Keyvisual Service', 'date' => now(), 'key_visual_filename' => 'slides/keyvisual.jpg', ]); ServiceAgendaItem::factory()->create([ 'service_id' => $service->id, 'title' => 'Begrüßung', 'service_song_id' => null, 'sort_order' => 1, 'is_before_event' => false, ]); $slideCountBefore = Slide::count(); $result = app(PlaylistExportService::class)->generatePlaylist($service); $playlist = ProPlaylistReader::read($result['path']); $embeddedProFiles = $playlist->getEmbeddedProFiles(); $fallbackSong = $playlist->getEmbeddedSong('Begrüßung.pro'); $this->assertCount(1, $embeddedProFiles); $this->assertNotNull($fallbackSong); $this->assertSame($slideCountBefore, Slide::count()); $slides = $this->allParserSlides($fallbackSong); $this->assertCount(1, $slides); $this->assertTrue($slides[0]->hasBackgroundMedia()); $this->assertSame('KEY_VISUAL.jpg', $slides[0]->getBackgroundMediaUrl()); $this->assertArrayHasKey('KEY_VISUAL.jpg', $playlist->getEmbeddedMediaFiles()); $this->assertSame('keyvisual-image', $playlist->getEmbeddedMediaFiles()['KEY_VISUAL.jpg']); $this->cleanupTempDir($result['temp_dir']); } public function test_song_agenda_item_does_not_get_keyvisual_fallback(): void { Storage::disk('public')->put('slides/keyvisual.jpg', 'keyvisual-image'); $service = Service::factory()->create([ 'title' => 'Song Service', 'date' => now(), 'key_visual_filename' => 'slides/keyvisual.jpg', ]); $song = $this->createSongWithContent('Nur ein Lied'); $serviceSong = ServiceSong::create([ 'service_id' => $service->id, 'song_id' => $song->id, 'cts_song_name' => 'Nur ein Lied', 'order' => 1, ]); ServiceAgendaItem::factory()->create([ 'service_id' => $service->id, 'title' => 'Nur ein Lied', 'service_song_id' => $serviceSong->id, 'sort_order' => 1, 'is_before_event' => false, ]); $result = app(PlaylistExportService::class)->generatePlaylist($service); $playlist = ProPlaylistReader::read($result['path']); $this->assertCount(1, $playlist->getEmbeddedProFiles()); $this->assertNotNull($playlist->getEmbeddedSong('Nur ein Lied.pro')); $this->assertNull($playlist->getEmbeddedSong('Keyvisual.pro')); $this->cleanupTempDir($result['temp_dir']); } public function test_sermon_agenda_item_with_uploaded_slides_prepends_keyvisual_sequence(): void { Storage::disk('public')->put('slides/keyvisual.jpg', 'keyvisual-image'); Storage::disk('public')->put('slides/sermon.jpg', 'sermon-image'); $service = Service::factory()->create([ 'title' => 'Slides Service', 'date' => now(), 'key_visual_filename' => 'slides/keyvisual.jpg', 'preacher_name' => 'Pastor Paul', 'preacher_name_override' => null, ]); $agendaItem = ServiceAgendaItem::factory()->create([ 'service_id' => $service->id, 'title' => 'Predigt', 'service_song_id' => null, 'sort_order' => 1, 'is_before_event' => false, ]); Slide::factory()->create([ 'service_id' => $service->id, 'service_agenda_item_id' => $agendaItem->id, 'type' => 'sermon', 'original_filename' => 'sermon.jpg', 'stored_filename' => 'slides/sermon.jpg', 'sort_order' => 0, ]); $result = app(PlaylistExportService::class)->generatePlaylist($service); $playlist = ProPlaylistReader::read($result['path']); $sermonSong = $playlist->getEmbeddedSong('Predigt.pro'); $this->assertCount(2, $playlist->getEmbeddedProFiles()); $this->assertNotNull($sermonSong); $names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries()); $this->assertContains('Keyvisual-Predigt', $names); $this->assertLessThan( array_search('Predigt', $names, true), array_search('Keyvisual-Predigt', $names, true), ); $slides = $this->allParserSlides($sermonSong); $this->assertCount(1, $slides); $this->assertFalse($slides[0]->hasBackgroundMedia()); $this->cleanupTempDir($result['temp_dir']); } public function test_without_keyvisual_empty_non_song_item_adds_no_playlist_entry(): void { $service = Service::factory()->create([ 'title' => 'Ohne Keyvisual', 'date' => now(), 'key_visual_filename' => null, ]); $song = $this->createSongWithContent('Vorhandenes Lied'); $serviceSong = ServiceSong::create([ 'service_id' => $service->id, 'song_id' => $song->id, 'cts_song_name' => 'Vorhandenes Lied', 'order' => 1, ]); ServiceAgendaItem::factory()->create([ 'service_id' => $service->id, 'title' => 'Begrüßung ohne Folien', 'service_song_id' => null, 'sort_order' => 1, 'is_before_event' => false, ]); ServiceAgendaItem::factory()->create([ 'service_id' => $service->id, 'title' => 'Vorhandenes Lied', 'service_song_id' => $serviceSong->id, 'sort_order' => 2, 'is_before_event' => false, ]); $result = app(PlaylistExportService::class)->generatePlaylist($service); $playlist = ProPlaylistReader::read($result['path']); $this->assertCount(1, $playlist->getEmbeddedProFiles()); $this->assertNotNull($playlist->getEmbeddedSong('Vorhandenes Lied.pro')); $this->assertNull($playlist->getEmbeddedSong('Begrüßung ohne Folien.pro')); $this->cleanupTempDir($result['temp_dir']); } private function createSongWithContent(string $title): Song { $song = Song::create([ 'title' => $title, 'ccli_id' => fake()->unique()->numerify('#####'), 'author' => 'Test Author', 'copyright_text' => 'Test Publisher', ]); $label = Label::firstOrCreate( ['name' => 'Verse 1 - '.$title], ['color' => '#2196F3'], ); $section = $song->sections()->create(['label_id' => $label->id, 'order' => 0]); $section->slides()->create(['order' => 0, 'text_content' => 'Erste Zeile']); $arrangement = $song->arrangements()->create(['name' => 'normal', 'is_default' => true]); $arrangement->arrangementSections()->create(['song_section_id' => $section->id, 'order' => 0]); return $song; } private function allParserSlides(\ProPresenter\Parser\Song $parserSong): array { $slides = []; foreach ($parserSong->getGroups() as $group) { foreach ($parserSong->getSlidesForGroup($group) as $slide) { $slides[] = $slide; } } return $slides; } private function cleanupTempDir(string $dir): void { if (! is_dir($dir)) { return; } $items = scandir($dir); if ($items === false) { return; } foreach ($items as $item) { if ($item === '.' || $item === '..') { continue; } $path = $dir.'/'.$item; is_dir($path) ? $this->cleanupTempDir($path) : unlink($path); } rmdir($dir); } }