- Fix probundle exports missing images (double slides/ prefix in storage path) - Replace manual ZipArchive with PresentationBundle + ProBundleWriter from parser plugin - Add per-agenda-item download route and buttons for songs and slide items - Remove text layer from image-only slides in .pro generation - Fix image conversion: upscale small images, black bars on 2 sides max (contain) - Add upload warnings for non-16:9 and sub-1920x1080 images (German, non-blocking) - Update SlideFactory and all tests to use slides/ prefix in stored_filename - Add 11 new tests (agenda download, image conversion, upload warnings)
126 lines
3.9 KiB
PHP
126 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Service;
|
|
use App\Models\ServiceAgendaItem;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use InvalidArgumentException;
|
|
use ProPresenter\Parser\PresentationBundle;
|
|
use ProPresenter\Parser\ProBundleWriter;
|
|
use ProPresenter\Parser\ProFileGenerator;
|
|
use RuntimeException;
|
|
|
|
class ProBundleExportService
|
|
{
|
|
private const ALLOWED_BLOCK_TYPES = ['information', 'moderation', 'sermon'];
|
|
|
|
public function generateBundle(Service $service, string $blockType): string
|
|
{
|
|
if (! in_array($blockType, self::ALLOWED_BLOCK_TYPES, true)) {
|
|
throw new InvalidArgumentException('Ungültiger Blocktyp für .probundle Export.');
|
|
}
|
|
|
|
$slides = $service->slides()
|
|
->where('type', $blockType)
|
|
->orderBy('sort_order')
|
|
->get();
|
|
|
|
$groupName = ucfirst($blockType);
|
|
|
|
return $this->buildBundleFromSlides($slides, $groupName);
|
|
}
|
|
|
|
public function generateAgendaItemBundle(ServiceAgendaItem $agendaItem): string
|
|
{
|
|
$agendaItem->loadMissing([
|
|
'slides',
|
|
'serviceSong.song.groups.slides',
|
|
'serviceSong.song.arrangements.arrangementGroups.group',
|
|
]);
|
|
|
|
$title = $agendaItem->title ?: 'Ablauf-Element';
|
|
|
|
if ($agendaItem->serviceSong?->song_id && $agendaItem->serviceSong->song) {
|
|
$song = $agendaItem->serviceSong->song;
|
|
|
|
if ($song->groups()->count() === 0) {
|
|
throw new RuntimeException('Song "'.$song->title.'" hat keine Gruppen.');
|
|
}
|
|
|
|
$parserSong = (new ProExportService)->generateParserSong($song);
|
|
$proFilename = self::safeFilename($song->title).'.pro';
|
|
|
|
$bundle = new PresentationBundle($parserSong, $proFilename);
|
|
$bundlePath = sys_get_temp_dir().'/'.uniqid('agenda-song-').'.probundle';
|
|
ProBundleWriter::write($bundle, $bundlePath);
|
|
|
|
return $bundlePath;
|
|
}
|
|
|
|
$slides = $agendaItem->slides()
|
|
->whereNull('deleted_at')
|
|
->orderBy('sort_order')
|
|
->get();
|
|
|
|
return $this->buildBundleFromSlides($slides, $title);
|
|
}
|
|
|
|
/** @param \Illuminate\Database\Eloquent\Collection<int, \App\Models\Slide> $slides */
|
|
private function buildBundleFromSlides($slides, string $groupName): string
|
|
{
|
|
$slideData = [];
|
|
$mediaFiles = [];
|
|
|
|
foreach ($slides as $slide) {
|
|
$sourcePath = Storage::disk('public')->path($slide->stored_filename);
|
|
if (! file_exists($sourcePath)) {
|
|
continue;
|
|
}
|
|
|
|
$imageFilename = basename($slide->stored_filename);
|
|
$imageContent = file_get_contents($sourcePath);
|
|
if ($imageContent === false) {
|
|
continue;
|
|
}
|
|
|
|
$mediaFiles[$imageFilename] = $imageContent;
|
|
|
|
$slideData[] = [
|
|
'media' => $imageFilename,
|
|
'format' => 'JPG',
|
|
'label' => $slide->original_filename,
|
|
];
|
|
}
|
|
|
|
$groups = [
|
|
[
|
|
'name' => $groupName,
|
|
'color' => [0, 0, 0, 1],
|
|
'slides' => $slideData,
|
|
],
|
|
];
|
|
|
|
$arrangements = [
|
|
[
|
|
'name' => 'normal',
|
|
'groupNames' => [$groupName],
|
|
],
|
|
];
|
|
|
|
$song = ProFileGenerator::generate($groupName, $groups, $arrangements);
|
|
$proFilename = self::safeFilename($groupName).'.pro';
|
|
|
|
$bundle = new PresentationBundle($song, $proFilename, $mediaFiles);
|
|
$bundlePath = sys_get_temp_dir().'/'.uniqid('bundle-').'.probundle';
|
|
ProBundleWriter::write($bundle, $bundlePath);
|
|
|
|
return $bundlePath;
|
|
}
|
|
|
|
private static function safeFilename(string $name): string
|
|
{
|
|
return preg_replace('/[^a-zA-Z0-9äöüÄÖÜß\-_ ]/', '', $name) ?: 'presentation';
|
|
}
|
|
}
|