martin6363/filament-video-engine
Filament Video Engine adds video handling to your Filament admin panels, letting you upload, preview, and manage video files directly from forms and tables. Integrate playback and media fields into your resources with minimal setup.
This is the first tagged release (v1.0.0), so there is no migration path to worry about yet — you are adopting a package at its initial API surface. Treat the public API as unstable until a 1.x patch history exists.
Install and register
composer require martin6363/filament-video-engine
php artisan vendor:publish --tag="filament-video-engine-config" # if offered
php artisan vendor:publish --tag="filament-video-engine-migrations" # if offered
php artisan migrate
Register the plugin on the panel you actually want it on, not globally:
use Martin6363\FilamentVideoEngine\FilamentVideoEnginePlugin;
public function panel(Panel $panel): Panel
{
return $panel
->plugin(FilamentVideoEnginePlugin::make());
}
Where to look first
README.md — the only reliable description of the field/column API at this version. Read it end-to-end before writing code, since there is no prior art or Stack Overflow coverage.config/filament-video-engine.php (after publishing) — disk, path prefix, accepted MIME types, player options. This is where 80% of "why doesn't my video show up" issues are resolved.src/ — check whether the package ships a Fields/ or Components/ namespace; that tells you whether it is a form-field package or a full page/widget package.v1.0.0 — the changelog link is empty of detail, so the commits are your only changelog.First use case
Attach a video to an existing Eloquent model and expose it in a Filament resource form. Start with the simplest possible shape — a single video per record — and confirm upload → storage → playback works before adding multiple videos, transcoding, or thumbnails.
VideoField::make('video')
->disk('public')
->directory('videos');
Run php artisan storage:link before your first upload attempt.
Model + cast alignment
Whatever column the field writes to, make sure the model treats it consistently. If the package stores a path string, a plain string column is fine. If it stores a JSON payload (path, mime, duration, thumbnail), cast it:
protected function casts(): array
{
return ['video' => 'array'];
}
Verify which one it is by inspecting the generated migration or the field's saveUploadedFileUsing/dehydrateStateUsing logic — do not assume.
Separate disks for ingest and delivery
Use a private/temp disk for uploads and a public (or CDN-backed) disk for playback. Keep these distinct in config so you can move to S3 + CloudFront later without touching resource classes.
Table columns and infolists
Pair the form field with the package's column/entry counterpart so the resource list shows a thumbnail and the view page shows a player. If the package only ships a form field, render playback yourself with a Blade view or Filament\Infolists\Components\ViewEntry, pointing at the stored URL:
ViewEntry::make('video')->view('filament.infolists.video-player')
Wrap writes in a transaction with the parent record
Video uploads are slow; a failed save leaves an orphaned file on disk. Either write the file first and attach in a DB::transaction(), or add a scheduled model:prune for orphans. This matters more than usual because re-uploading a large file is expensive for the user.
Authorization
The field inherits the resource policy, but storage authorization is separate. If you use a private disk, playback needs a signed temporary URL — check whether the package exposes a ->temporaryUrl() / ->visibility('private') style toggle before you build your own route.
Multi-tenancy / panel scoping
If you use Filament tenancy, confirm the directory the package writes to is tenant-scoped. A global videos/ prefix means tenant A's files sit next to tenant B's; that is fine for paths but not for private-disk access. Pass tenant ID into the directory explicitly:
->directory(fn () => 'videos/' . filament()->getTenant()?->getKey())
v1.0.0 means zero backwards-compatibility guarantees. Pin the exact version ("martin6363/filament-video-engine": "1.0.0") in composer.json rather than a caret constraint. The empty changelog gives you no signal about how the maintainer intends to version; assume minor bumps may break.
Filament major version compatibility is unstated. Verify whether the plugin targets Filament v3 or v4 before installing — the plugin registration, field base classes, and asset registration differ. A mismatch usually surfaces as a Class not found or a blank field rather than a clear error. Check composer.json of the package for its filament/filament constraint.
Upload limits are the most likely first bug. Livewire's temporary upload limit, PHP's upload_max_filesize / post_max_size, and any web server body limit all apply before the package sees the file. A 200 MB video will silently fail at any of these layers. Set all three, and prefer chunked uploads if the package supports them.
Do not do video processing inline. If the package offers thumbnails or transcoding, ensure it dispatches a job. Synchronous FFmpeg calls inside a Livewire request will hit max_execution_time and take the panel down for that user. Queue it, and make the player degrade gracefully when the poster/thumbnail is not ready yet.
Publishing tags are guesses until confirmed. vendor:publish tags differ per package. Run php artisan vendor:publish with no arguments and read the list — it is faster than guessing tag names.
Debugging checklist, in order:
storage/app/public/videos/...public/storagedisk match the disk the file landed on?APP_URL? A mismatch here produces a broken player with no error.->dehydrated() / ->dehydrateStateUsing() overrides from another developer on the resource are a common cause of "upload works, nothing saves."Extension points to probe early. Look for published Blade views and whether the field exposes ->view() / ->playerAttributes(). If neither exists, wrapping the field is unpleasant — a custom field that composes the package's field is the safer route than subclassing it, since subclass internals can change in 1.1.0 without notice.
Contribution risk. A single-tag package with a one-line changelog and no prior assessment history is a dependency you should be prepared to fork. Keep your integration thin — one custom field class or one Blade partial that calls into the package — so replacing it later is a localized change rather than a resource-wide rewrite.
How can I help you explore Laravel packages today?