thats-me/backend/app/Http/Requests/StoreEventMediaRequest.php
Kevin Adametz b262a6771f Backend: Medien-Typen, Sprachnotizen, Event-Felder und Sync-Härtung
- Event-Medien tragen Typ (image/video/audio), Dauer und Notiz; Videos
  bekommen ein Poster, Audio wird per ffmpeg nach AAC (audio/mp4)
  gewandelt, Originale werden mit Range-Unterstützung gestreamt.
- Upload nimmt eine Notiz (caption) direkt mit, PATCH ändert sie später.
- Neue Event-Felder: Zeitraum (end_date), Kategorie, Personen, Meilenstein.
  Regeln zentral in EventFields, damit CRUD und Sync gleich prüfen.
- Titel darf leer sein: ein Event entsteht mit dem ersten Eintrag.
- Artisan-Befehl media:transcode-voice-notes wandelt alte WebM/Ogg-Notizen,
  die Safari nicht abspielen kann.
- Login-Throttling, Settings-Validierung, Sync-Fehlercodes ohne Rohtext.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 07:00:35 +00:00

70 lines
2.5 KiB
PHP

<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreEventMediaRequest extends FormRequest
{
/** Images accepted everywhere an image is allowed. */
private const IMAGE_MIMES = 'jpg,jpeg,png,webp';
/** Container formats phones and browsers actually produce. */
private const VIDEO_MIMETYPES = 'video/mp4,video/quicktime,video/webm,video/x-m4v';
/**
* MediaRecorder output differs per browser: Chrome and Firefox record
* audio into a WebM container, which libmagic reports as video/webm;
* Safari records audio/mp4 (often sniffed as audio/x-m4a or video/mp4).
*/
private const AUDIO_MIMETYPES = 'audio/webm,video/webm,audio/ogg,application/ogg,audio/mp4,audio/x-m4a,video/mp4,audio/mpeg,audio/wav,audio/x-wav,audio/aac';
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* The accepted file type depends on the collection: the key image stays
* an image, the gallery takes images and videos, and a voice note is
* audio only.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
$file = match ($this->input('collection', 'gallery')) {
'voice_note' => ['required', 'file', 'mimetypes:'.self::AUDIO_MIMETYPES, 'max:20480'],
'gallery' => [
'required',
'file',
'mimetypes:image/jpeg,image/png,image/webp,'.self::VIDEO_MIMETYPES,
'max:102400',
],
default => ['required', 'image', 'mimes:'.self::IMAGE_MIMES, 'max:10240'],
};
return [
'file' => $file,
'collection' => ['nullable', 'in:key_image,gallery,voice_note'],
// Sent along with the file when the note was written before the
// upload could happen (offline), so it needs no second request.
'caption' => ['nullable', 'string', 'max:500'],
];
}
/**
* @return array<string, string>
*/
public function messages(): array
{
return [
'file.mimetypes' => 'Dieses Dateiformat wird hier nicht unterstützt.',
'file.mimes' => 'Dieses Dateiformat wird hier nicht unterstützt.',
'file.image' => 'Für das Key Image ist nur ein Bild erlaubt.',
'file.max' => 'Die Datei ist zu groß.',
];
}
}