diff --git a/app/Http/Requests/StoreRecordingRequest.php b/app/Http/Requests/StoreRecordingRequest.php index 3e3e8fb..093fecc 100644 --- a/app/Http/Requests/StoreRecordingRequest.php +++ b/app/Http/Requests/StoreRecordingRequest.php @@ -3,9 +3,32 @@ namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rules\File; class StoreRecordingRequest extends FormRequest { + /** + * Popular audio extensions Whisper-compatible providers typically accept. + * + * @var list + */ + public const AUDIO_EXTENSIONS = [ + 'mp3', + 'mpeg', + 'mpga', + 'wav', + 'ogg', + 'oga', + 'flac', + 'm4a', + 'mp4', + 'aac', + 'webm', + 'wma', + 'aiff', + 'aif', + ]; + public function authorize(): bool { return true; @@ -17,7 +40,10 @@ class StoreRecordingRequest extends FormRequest public function rules(): array { return [ - 'audio' => ['required', 'file', 'mimes:mp3,mpeg', 'max:102400'], + 'audio' => [ + 'required', + File::types(self::AUDIO_EXTENSIONS)->max(102400), + ], 'title' => ['nullable', 'string', 'max:255'], ]; } @@ -28,8 +54,8 @@ class StoreRecordingRequest extends FormRequest public function messages(): array { return [ - 'audio.required' => 'Please choose an MP3 file to upload.', - 'audio.mimes' => 'Only MP3 files are supported.', + 'audio.required' => 'Please choose an audio file to upload.', + 'audio' => 'Unsupported audio type. Use MP3, WAV, OGG, FLAC, M4A, AAC, WebM, WMA, or AIFF.', 'audio.max' => 'The audio file may not be larger than 100 MB.', ]; } diff --git a/app/Services/Mp3MetadataService.php b/app/Services/Mp3MetadataService.php index 2dc1abb..afb967f 100644 --- a/app/Services/Mp3MetadataService.php +++ b/app/Services/Mp3MetadataService.php @@ -8,7 +8,7 @@ use getID3; class Mp3MetadataService { /** - * Extract ID3 and audio metadata from an MP3 file on disk. + * Extract tags and duration from a common audio file (MP3, WAV, OGG, FLAC, M4A, etc.). * * @return array{ * title: ?string, @@ -23,17 +23,15 @@ class Mp3MetadataService $analyzer = new getID3; $info = $analyzer->analyze($absolutePath); - $tags = []; - if (isset($info['tags']['id3v2'])) { - $tags = $info['tags']['id3v2']; - } elseif (isset($info['tags']['id3v1'])) { - $tags = $info['tags']['id3v1']; - } + $tags = $this->preferredTags($info); $title = $this->firstTag($tags, 'title'); $artist = $this->firstTag($tags, 'artist'); $album = $this->firstTag($tags, 'album'); - $year = $this->firstTag($tags, 'year') ?? $this->firstTag($tags, 'recording_time'); + $year = $this->firstTag($tags, 'year') + ?? $this->firstTag($tags, 'date') + ?? $this->firstTag($tags, 'recording_time') + ?? $this->firstTag($tags, 'creation_date'); $duration = isset($info['playtime_seconds']) ? (int) round((float) $info['playtime_seconds']) @@ -57,6 +55,38 @@ class Mp3MetadataService ]; } + /** + * Pick the richest tag set getID3 found for this format. + * + * @param array $info + * @return array + */ + private function preferredTags(array $info): array + { + $priority = [ + 'id3v2', + 'id3v1', + 'vorbiscomment', + 'quicktime', + 'riff', + 'asf', + 'ape', + 'matroska', + ]; + + foreach ($priority as $format) { + if (! empty($info['tags'][$format]) && is_array($info['tags'][$format])) { + return $info['tags'][$format]; + } + } + + if (! empty($info['comments']) && is_array($info['comments'])) { + return $info['comments']; + } + + return []; + } + /** * @param array $tags */ diff --git a/resources/views/recordings/create.blade.php b/resources/views/recordings/create.blade.php index bb14124..f782c48 100644 --- a/resources/views/recordings/create.blade.php +++ b/resources/views/recordings/create.blade.php @@ -5,7 +5,7 @@ @section('content')

Upload recording

-

Upload an MP3 from your pocket recorder. ID3 metadata is extracted automatically.

+

Upload audio from your pocket recorder. Embedded metadata is extracted when available.

- + +

MP3, WAV, OGG, FLAC, M4A, AAC, WebM, WMA, or AIFF (max 100 MB).

@@ -35,7 +36,7 @@ type="text" name="title" value="{{ old('title') }}" - placeholder="Leave blank to use ID3 title or filename" + placeholder="Leave blank to use embedded title or filename" class="mt-2 w-full rounded border border-stone-300 px-3 py-2 text-sm shadow-sm focus:border-teal-600 focus:outline-none focus:ring-1 focus:ring-teal-600" >