put('slides/kv.jpg', 'keyvisual-image'); $service = $this->serviceWithSong('Mit Countdown', 'slides/kv.jpg'); $result = app(PlaylistExportService::class)->generatePlaylist($service); $playlist = ProPlaylistReader::read($result['path']); $entries = $playlist->getEntries(); $first = $entries[0]; $this->assertSame('Countdown', $first->getName(), 'Countdown must be the very first playlist item'); $this->assertTrue($first->isPresentation()); $countdownSong = $playlist->getEmbeddedSong($first->getDocumentFilename()); $this->assertNotNull($countdownSong, 'Embedded Countdown .pro missing'); $slides = $this->allParserSlides($countdownSong); $this->assertCount(1, $slides); $slide = $slides[0]; $this->assertTrue($slide->hasClock(), 'Countdown slide must carry a live clock element'); $this->assertSame('HH:mm', $slide->getClockFormat()); $this->assertTrue($slide->hasBackgroundMedia(), 'Countdown slide must have the key-visual as background'); $this->assertSame('KEY_VISUAL.jpg', $slide->getBackgroundMediaUrl()); $embeddedMedia = $playlist->getEmbeddedMediaFiles(); $this->assertArrayHasKey('KEY_VISUAL.jpg', $embeddedMedia, 'Key-visual must be embedded under fixed name'); $this->assertSame('keyvisual-image', $embeddedMedia['KEY_VISUAL.jpg']); $this->cleanupTempDir($result['temp_dir']); } public function test_no_countdown_slide_when_setting_disabled(): void { Setting::set('add_countdown_slide', '0'); Storage::disk('public')->put('slides/kv.jpg', 'keyvisual-image'); $service = $this->serviceWithSong('Ohne Countdown', 'slides/kv.jpg'); $result = app(PlaylistExportService::class)->generatePlaylist($service); $playlist = ProPlaylistReader::read($result['path']); $names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries()); $this->assertNotContains('Countdown', $names); $this->cleanupTempDir($result['temp_dir']); } public function test_no_countdown_slide_when_enabled_but_no_keyvisual(): void { Setting::set('add_countdown_slide', '1'); $service = $this->serviceWithSong('Countdown ohne KV', null); $result = app(PlaylistExportService::class)->generatePlaylist($service); $playlist = ProPlaylistReader::read($result['path']); $names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries()); $this->assertNotContains('Countdown', $names); $this->cleanupTempDir($result['temp_dir']); } private function serviceWithSong(string $title, ?string $keyVisualFilename): Service { $service = Service::factory()->create([ 'title' => $title, 'date' => now(), 'key_visual_filename' => $keyVisualFilename, 'background_filename' => null, 'has_agenda' => true, ]); $song = $this->createSongWithContent($title.' Lied'); $serviceSong = ServiceSong::create([ 'service_id' => $service->id, 'song_id' => $song->id, 'cts_song_name' => $title.' Lied', 'order' => 1, ]); ServiceAgendaItem::factory()->create([ 'service_id' => $service->id, 'title' => $title.' Lied', 'service_song_id' => $serviceSong->id, 'sort_order' => 1, 'is_before_event' => false, ]); return $service; } 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); } }