- 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>
70 lines
2.5 KiB
PHP
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ß.',
|
|
];
|
|
}
|
|
}
|