whereIn('transcription_status', ['pending', 'processing']) ->orderBy('id') ->each(fn (Recording $recording) => $recording->recoverOrphanedTranscription()); $query = Recording::query()->latest(); if ($search = $request->string('q')->trim()->toString()) { $query->search($search); } $recordings = $query->paginate(20)->withQueryString(); $pendingCount = Recording::query() ->whereIn('transcription_status', ['pending', 'failed', 'cancelled']) ->get() ->filter(fn (Recording $recording) => ! $recording->hasActiveTranscriptionJob()) ->count(); return view('recordings.index', [ 'recordings' => $recordings, 'search' => $search ?? '', 'pendingCount' => $pendingCount, ]); } /** * Show the upload form. */ public function create(): View { return view('recordings.create'); } /** * Store one or more uploaded recordings. */ public function store(StoreRecordingRequest $request, Mp3MetadataService $metadata): RedirectResponse { /** @var list $files */ $files = array_values(array_filter( $request->file('audio', []), fn ($file) => $file instanceof UploadedFile, )); $titleOverride = $request->string('title')->trim()->toString(); $recordings = []; foreach ($files as $file) { $title = count($files) === 1 && $titleOverride !== '' ? $titleOverride : null; $recordings[] = $this->storeUploadedRecording($file, $metadata, $title); } if (count($recordings) === 1) { return redirect() ->route('recordings.show', $recordings[0]) ->with('success', 'Recording uploaded — transcription queued.'); } return redirect() ->route('recordings.index') ->with('success', count($recordings).' recordings uploaded — transcription queued.'); } /** * Display the specified recording. */ public function show(Recording $recording): View { $recording->recoverOrphanedTranscription(); $recording->refresh(); return view('recordings.show', compact('recording')); } /** * Remove the specified recording. */ public function destroy(Recording $recording): RedirectResponse { $recording->deleteFile(); $recording->delete(); return redirect() ->route('recordings.index') ->with('success', 'Recording deleted.'); } /** * Persist a single uploaded audio file as a recording. */ private function storeUploadedRecording( UploadedFile $file, Mp3MetadataService $metadata, ?string $titleOverride = null, ): Recording { $path = $file->store('recordings', 'local'); $absolutePath = Storage::disk('local')->path($path); $tags = $metadata->extract($absolutePath); $title = $titleOverride ?: ($tags['title'] ?? pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME)); $recording = Recording::create([ 'title' => $title, 'original_filename' => $file->getClientOriginalName(), 'file_path' => $path, 'duration_seconds' => $tags['duration_seconds'], 'recorded_at' => $tags['recorded_at'], 'artist' => $tags['artist'], 'album' => $tags['album'], 'file_size_bytes' => $file->getSize() ?: 0, 'transcription_status' => 'pending', 'transcription_driver' => 'local', ]); $recording->queueLocalTranscription(); return $recording->fresh(); } }