goodappr / moonshine-media-fields
Audio, AudioList, Video, VideoList fields for MoonShine 4.x - media with preview and modals
Package info
github.com/GoodAppR/moonshine-media-fields
Language:Blade
pkg:composer/goodappr/moonshine-media-fields
Requires
- php: ^8.2
- moonshine/moonshine: ^4.0
README
Audio, AudioList, Video and VideoList fields for MoonShine 4.x with preview, playback modals and custom audio player.
Table of Contents
Requirements
- PHP 8.2+
- Laravel 11+
- MoonShine 4.x
- Alpine.js (included with MoonShine)
Installation
composer require goodappr/moonshine-media-fields
The package auto-registers MediaFieldsServiceProvider.
Configuration
Publish config (optional)
php artisan vendor:publish --tag=moonshine-media-fields-config
Creates config/media_fields.php:
<?php return [ 'audio' => [ 'extensions' => ['mp3', 'wav', 'ogg', 'm4a', 'aac', 'flac', 'webm'], 'max_size_kb' => 20480, 'dir' => 'audio', 'default_preload' => 'metadata', ], 'video' => [ 'extensions' => ['mp4', 'mov', 'avi', 'mkv', 'webm', 'flv', 'wmv'], 'max_size_kb' => 102400, 'dir' => 'video', 'default_preload' => 'metadata', ], ];
Migration example
For storing audio and video in DB (string or json types):
Schema::table('posts', function (Blueprint $table) { $table->string('audio')->nullable(); $table->json('audio_list')->nullable(); $table->string('video')->nullable(); $table->json('video_list')->nullable(); $table->json('video_list_posters')->nullable(); });
Usage
Audio — single audio
Form:
- File upload
showPreview(true)— player and preview (default)showPreview(false)— filename only
Index:
- Play button → modal with player
editOnIndex(true)— edit icon linking to formdownloadOnIndex(true)— download icondownloadPreviewModal(true)— download button in modalshowFileName(bool)— show filename
use MoonShine\MediaFields\Fields\Audio; Audio::make('Audio', 'audio') ->dir('uploads/audio') ->editOnIndex(true) ->downloadOnIndex(true) ->downloadPreviewModal(true) ->showFileName(true) ->preload('metadata') ->loop(false);
AudioList — multiple audio
Form:
- Multiple file selection
maxFiles(int)— max countmaxFileSizeKb(int)— max size per fileshowPreview(true)— player for eachmaxHeightContainer(string)— container max-height (e.g.'400px')
Index:
- Play button + count → modal with list
downloadOnIndex(true)— download for each file
use MoonShine\MediaFields\Fields\AudioList; AudioList::make('Audio list', 'audio_list') ->dir('uploads/audio') ->maxFiles(10) ->maxFileSizeKb(5120) ->editOnIndex(true) ->downloadOnIndex(true) ->maxHeightContainer('400px');
Video — single video
Form:
- Upload, preview with video player
poster(string)— static poster URLposterField(Image $field)— poster as embedded Image field
Index:
- With poster: thumbnail + play button
- Without poster: camera icon
videoMaxHeight(string)— max-height for video in modal
use MoonShine\MediaFields\Fields\Video; Video::make('Video', 'video') ->dir('uploads/video') ->poster('/images/poster.jpg') ->editOnIndex(true) ->videoMaxHeight('400px');
With poster via separate column:
Video::make('Video', 'video') ->dir('uploads/video') ->posterColumn('video_poster') ->posterDir('uploads/posters');
VideoList — multiple video
Form:
- Video carousel or grid
maxFiles,maxFileSizeKbgridColumns(2|3|4|null)— 2–4 columns ornullfor carouselposterField(VideoListPosters $field)— poster per video
Index:
- Camera icon + count or poster thumbnails
videoMaxHeight(string),gridColumns(int|null)
use MoonShine\MediaFields\Fields\VideoList; use MoonShine\MediaFields\Fields\VideoListPosters; VideoList::make('Videos', 'videos') ->dir('uploads/videos') ->maxFiles(5) ->editOnIndex(true) ->videoMaxHeight('320px') ->gridColumns(2) ->posterField( VideoListPosters::make('Posters', 'video_list_posters') ->videosColumn('videos') );
VideoListPosters — posters for VideoList
Separate field for uploading posters for each video. Use with VideoList::posterField().
VideoListPosters::make('Video posters', 'video_list_posters') ->videosColumn('videos');
Example Resource
<?php namespace App\MoonShine\Resources; use App\Models\Post; use MoonShine\MediaFields\Fields\Audio; use MoonShine\MediaFields\Fields\AudioList; use MoonShine\MediaFields\Fields\Video; use MoonShine\MediaFields\Fields\VideoList; use MoonShine\MediaFields\Fields\VideoListPosters; use MoonShine\Resources\ModelResource; class PostResource extends ModelResource { protected string $model = Post::class; public function fields(): array { return [ Audio::make('Audio', 'audio') ->dir('uploads/audio') ->editOnIndex(true) ->downloadOnIndex(true), AudioList::make('Audio list', 'audio_list') ->dir('uploads/audio') ->maxFiles(5), Video::make('Video', 'video') ->dir('uploads/video') ->editOnIndex(true) ->videoMaxHeight('400px'), VideoList::make('Videos', 'videos') ->dir('uploads/videos') ->maxFiles(5) ->gridColumns(2) ->posterField( VideoListPosters::make('Posters', 'video_list_posters') ->videosColumn('videos') ), ]; } }
Themes and Styles
Uses MoonShine CSS variables:
--color-base,--color-base-text,--color-base-stroke--color-primary,--color-primary-text
Supports light and dark themes via .dark and [data-theme="dark"].
Troubleshooting
403 Forbidden when playing audio/video
Server may block MIME types. Check:
- Apache —
.htaccessor config:
AddType audio/mpeg .mp3 AddType video/mp4 .mp4
-
ModSecurity — disable or adjust rules for media files.
-
Nginx — ensure
typesincludes required MIME types.
Clear cache after update
php artisan config:clear php artisan view:clear
License
MIT. See LICENSE.




