lara-zeus/echoo
Echoo is a lightweight Laravel package from the Lara Zeus suite that simplifies real-time broadcasting and notifications. Connect Laravel Echo, fire events, and keep users in sync with an expressive, minimal API and almost no boilerplate.
Install the package and its assets:
composer require lara-zeus/echoo
php artisan vendor:publish --tag=zeus-echoo-assets # check the tag used by your version
php artisan storage:link
Confirm the package is registered. Lara Zeus plugins are auto-discovered via composer.json extra.laravel.providers; if you maintain a cached config, run php artisan optimize:clear after install.
Look first at the package's config/echoo.php (after publishing) and its Filament field/component class — that's where disk, directory, accepted mime types and the JS recorder options live. The README's Filament section is the fastest path to a working field.
First use case — drop a recorder into an existing Filament form:
use LaraZeus\Echoo\...\AudioRecorder; // adjust to the class shipped in your version
AudioRecorder::make('voice_note')
->disk('public')
->directory('voice-notes')
->required(),
Then record, save, and verify the file landed under storage/app/public/voice-notes and the model attribute stores the relative path.
Treat the field as a file-upload field with a recorder UI. Everything you already do for FileUpload (disk, visibility, maxSize, acceptedFileTypes, imagePreview-style previews) maps across. Model accessors, observer-based cleanup, and Storage::disk(...)->url() all work unchanged.
Existing vs. new records. 1.0.4 fixes preview URL generation for audio that has not been persisted yet. Keep two code paths in mind: an unsaved recording lives as a temporary upload and needs a temporary/preview URL, while a saved one resolves through the configured disk. If you wrap the field or override the preview in a custom view, resolve the URL from the component rather than hand-rolling Storage::url($state) — the latter returns a broken path for temp files.
Pair with a plain FileUpload for import. Letting users either record or upload the same audio is a common requirement; point both fields at the same column and normalize in a mutateFormDataBeforeSave/beforeSave hook, or use a single field with recorder + upload enabled.
Playback in tables and infolists. Use the package's column/entry counterpart if one ships with your version; otherwise an Audio/custom view column that calls Storage::disk($disk)->url($state) is sufficient. For private disks, always go through temporaryUrl() so signed URLs are generated per request.
S3 / private buckets. Set visibility explicitly (private is the safe default for voice notes) and make sure the preview component uses temporaryUrl() with a sensible expiry — otherwise you'll see 403s in the recorder preview while the saved file plays fine elsewhere.
Validation is your job. Browser recording produces webm/opus on Chrome and mp4/aac on Safari by default. If downstream tooling (Whisper transcription, ffmpeg, a mobile app) needs a stable container, validate/normalize on save with a queued job instead of trusting the client mime type.
Cleanup. Deleting the model does not delete the file. Add a deleted model event (or ForceDelete) that calls Storage::disk($disk)->delete($path), and prune orphaned temp uploads on a schedule if you allow abandoning forms.
navigator.mediaDevices.getUserMedia is unavailable on plain http:// origins (except localhost). A recorder that silently does nothing in staging is almost always the missing TLS or an unaccepted mic permission — check the browser console before touching package code.composer update, re-run the publish command with --force and hard-refresh. The 1.0.4 preview-URL fix in particular only takes effect if you also ship the updated front-end asset.storage:link and disk visibility. Files written to public but saved with private visibility 404 via Storage::url(). Keep disk, visibility, and the preview resolver consistent — this is the second most common cause of "recorded but won't play".config/echoo.php values are ignored at runtime under php artisan config:cache. If changing the directory or disk has no effect, you're looking at a stale cache.->visible() / ->hidden() based on another field) can tear down the recorder element mid-recording due to a Livewire DOM diff. Prefer ->dehydrated(false) plus server-side guards, or gate via a separate step in a wizard rather than reactive visibility.storage/logs/laravel.log (Livewire validation on maxSize/acceptedFileTypes) → the target disk directly. Most issues are permission, mime, or size validation rather than the recorder itself.^1.0 and read the changelog before minor jumps; the "chore: GH actions and policies" entries are noise, but the audio-preview fix is the one behavioural change in 1.0.4 worth verifying against your own preview implementation.How can I help you explore Laravel packages today?