Files
ladill-meet/app/Http/Controllers/Meet/RecordingController.php
T
isaaccladandCursor 4af53f9374
Deploy Ladill Meet / deploy (push) Successful in 52s
Fix stuck recording processing and add invoice service API test.
Capture stage spotlight video for browser recordings, fail incomplete uploads, allow post-session host uploads, and expire stale processing recordings automatically.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-03 22:54:26 +00:00

84 lines
3.2 KiB
PHP

<?php
namespace App\Http\Controllers\Meet;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Meet\Concerns\ScopesToAccount;
use App\Models\Recording;
use App\Services\Meet\RecordingService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;
class RecordingController extends Controller
{
use ScopesToAccount;
public function __construct(
protected RecordingService $recordings,
) {}
public function index(Request $request): View
{
$this->authorizeAbility($request, 'meetings.view');
$owner = $this->ownerRef($request);
$organization = $this->organization($request);
$recordings = Recording::owned($owner)
->whereHas('session.room', fn ($q) => $q->where('organization_id', $organization->id))
->with(['session.room'])
->orderByDesc('created_at')
->paginate(20);
return view('meet.recordings.index', compact('recordings'));
}
public function show(Request $request, Recording $recording): View
{
$this->authorizeAbility($request, 'meetings.view');
$this->authorizeOwner($request, $recording);
$recording->load(['session.room', 'session.aiSummaries', 'session.participants']);
$recording = $this->recordings->resolveProcessingState($recording);
$canManage = app(\App\Services\Meet\MeetPermissions::class)
->can($this->member($request), 'meetings.manage');
return view('meet.recordings.show', compact('recording', 'canManage'));
}
public function download(Request $request, Recording $recording): StreamedResponse
{
$this->authorizeAbility($request, 'meetings.view');
$this->authorizeOwner($request, $recording);
$recording = $this->recordings->resolveProcessingState($recording);
abort_unless($recording->hasPlayableFile(), 404, 'Recording file is not available.');
abort_unless(app(\App\Services\Meet\MeetBillingService::class)->storageIsAccessible($recording), 402, 'Recording storage billing is overdue. Top up your Ladill wallet to download this recording.');
$disk = Storage::disk(config('meet.recordings.disk', 'local'));
$extension = strtolower(pathinfo($recording->storage_path, PATHINFO_EXTENSION) ?: 'webm');
$mime = match ($extension) {
'webm' => 'video/webm',
'mp4' => 'video/mp4',
'mkv' => 'video/x-matroska',
default => 'application/octet-stream',
};
$filename = \Illuminate\Support\Str::slug($recording->session->room->title).'.'.$extension;
return response()->streamDownload(function () use ($disk, $recording) {
echo $disk->get($recording->storage_path);
}, $filename, ['Content-Type' => $mime]);
}
public function destroy(Request $request, Recording $recording): RedirectResponse
{
$this->authorizeAbility($request, 'meetings.manage');
$this->authorizeOwner($request, $recording);
$this->recordings->delete($recording, $this->ownerRef($request));
return redirect()->route('meet.recordings.index')->with('success', 'Recording deleted.');
}
}