https://github.com/iamgerwin/nova-media-hub
A comprehensive media management package for Laravel Nova with advanced chunked upload support for handling large files efficiently.
https://github.com/iamgerwin/nova-media-hub
filemanager media mediahub nova
Last synced: 6 months ago
JSON representation
A comprehensive media management package for Laravel Nova with advanced chunked upload support for handling large files efficiently.
- Host: GitHub
- URL: https://github.com/iamgerwin/nova-media-hub
- Owner: iamgerwin
- License: mit
- Created: 2025-10-13T17:45:06.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2025-10-13T18:20:06.000Z (6 months ago)
- Last Synced: 2025-10-13T18:56:54.090Z (6 months ago)
- Topics: filemanager, media, mediahub, nova
- Language: PHP
- Homepage:
- Size: 0 Bytes
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Nova Media Hub
[](https://github.com/iamgerwin/nova-media-hub/actions)
[](https://packagist.org/packages/iamgerwin/nova-media-hub)
[](https://packagist.org/packages/iamgerwin/nova-media-hub)
[](https://packagist.org/packages/iamgerwin/nova-media-hub)
A comprehensive media management package for [Laravel Nova](https://nova.laravel.com) with advanced chunked upload support for handling large files efficiently.
## Table of Contents
- [Features](#features)
- [Requirements](#requirements)
- [Package Structure](#package-structure)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
- [Basic Usage](#basic-usage)
- [Field Configuration](#field-configuration)
- [Model Integration](#model-integration)
- [Chunked Upload API](#chunked-upload-api)
- [Advanced Use Cases](#advanced-use-cases)
- [Edge Cases](#edge-cases)
- [Testing](#testing)
- [Security](#security)
- [Maintenance](#maintenance)
- [Contributing](#contributing)
- [Issues & Support](#issues--support)
- [Credits](#credits)
- [License](#license)
## Features
### Core Features
- **Media Hub Interface** - Dedicated Nova tool for managing media assets
- **Media Hub Field** - Custom field for selecting single or multiple media items
- **Image Optimization** - Automatic optimization and multiple format conversions
- **Collections** - Organize media into logical collections
- **Dark Mode Support** - Full support for Nova's dark mode
- **Localization** - Multi-language support with translation loader
- **Custom Fields** - Extensible metadata fields (e.g., copyright, alt text)
### Chunked Upload System
- **Large File Support** - Upload files up to 1GB+ without server configuration changes
- **Real-time Progress** - Visual progress tracking with percentage display
- **Automatic Retry** - Intelligent retry logic for failed chunk uploads
- **Smart Fallback** - Automatic fallback to standard upload for small files
- **Memory Efficient** - Streaming-based chunk combination prevents memory exhaustion
- **Session Management** - Cache-based upload state persistence
- **Configurable** - Adjustable chunk sizes, retry attempts, and thresholds
### Developer Features
- **Multi-version Compatibility** - Supports PHP 8.2-8.3, Laravel 10-12, Nova 4-5
- **Extensive Testing** - Comprehensive test suite with CI/CD pipeline
- **Type Safety** - Full type hints and strict type checking
- **Clean API** - Intuitive, well-documented API
- **Extensible** - Hooks and events for customization
## Requirements
| Component | Version |
|-----------|---------|
| PHP | 8.2, 8.3+ |
| Laravel | 10.x, 11.x, 12.x |
| Laravel Nova | 4.x, 5.x |
## Package Structure
```
nova-media-hub/
├── .github/
│ └── workflows/
│ └── tests.yml # CI/CD pipeline
├── config/
│ └── nova-media-hub.php # Main configuration file
├── database/
│ └── migrations/
│ ├── 2022_06_15_000000_create_media_library_table.php
│ └── 2024_01_29_000000_add_nova_media_hub_indexes.php
├── dist/ # Compiled assets
│ ├── css/
│ │ └── entry.css
│ ├── js/
│ │ ├── entry.js
│ │ └── entry.js.LICENSE.txt
│ └── mix-manifest.json
├── docs/ # Documentation images
│ ├── choose-media-dark.jpeg
│ └── media-hub-dark.jpeg
├── lang/ # Translation files
│ ├── en.json
│ ├── fa.json
│ └── it.json
├── resources/
│ ├── css/
│ │ └── entry.css # Source styles
│ └── js/
│ ├── api.js # API client
│ ├── entry.js # Main entry point
│ ├── components/ # Vue components
│ │ ├── ChunkedFileUpload.vue
│ │ ├── DropZone.vue
│ │ ├── MediaItem.vue
│ │ ├── MediaItemContextMenu.vue
│ │ ├── MediaOrderSelect.vue
│ │ ├── MediaViewModalInfoListItem.vue
│ │ ├── ModalFilterItem.vue
│ │ └── PaginationLinks.vue
│ ├── composables/ # Vue composables
│ │ └── useDragAndDrop.js
│ ├── fields/ # Nova field components
│ │ └── MediaField/
│ │ ├── DetailMediaHubField.vue
│ │ ├── FormMediaHubField.vue
│ │ └── IndexMediaHubField.vue
│ ├── icons/ # SVG icon components
│ │ ├── AudioIcon.vue
│ │ ├── CheckMarkIcon.vue
│ │ ├── OtherIcon.vue
│ │ └── VideoIcon.vue
│ ├── mixins/ # Vue mixins
│ │ ├── HandlesMediaHubFieldValue.js
│ │ ├── HandlesMediaLists.js
│ │ └── HandlesMediaUpload.js
│ ├── modals/ # Modal components
│ │ ├── ChooseMediaModal.vue
│ │ ├── ConfirmDeleteModal.vue
│ │ ├── MediaReplaceModal.vue
│ │ ├── MediaUploadModal.vue
│ │ ├── MediaViewModal.vue
│ │ └── MoveToCollectionModal.vue
│ ├── utils/ # Utility classes
│ │ └── ChunkedUploader.js # Chunked upload handler
│ └── views/ # Main views
│ └── NovaMediaHub.vue
├── routes/
│ └── api.php # API routes
├── src/
│ ├── Casts/
│ │ └── MediaCast.php # Eloquent cast
│ ├── Console/
│ │ └── Commands/
│ │ └── CleanupOldChunksCommand.php # Cleanup command
│ ├── Exceptions/ # Custom exceptions
│ │ ├── DiskDoesNotExistException.php
│ │ ├── FileDoesNotExistException.php
│ │ ├── FileTooLargeException.php
│ │ ├── FileValidationException.php
│ │ ├── MimeTypeNotAllowedException.php
│ │ ├── NoFileProvidedException.php
│ │ └── UnknownFileTypeException.php
│ ├── Filters/ # Nova filters
│ │ ├── Collection.php
│ │ ├── Search.php
│ │ └── Sort.php
│ ├── Http/
│ │ ├── Controllers/
│ │ │ ├── ChunkedMediaUploadController.php
│ │ │ └── MediaHubController.php
│ │ └── Middleware/
│ │ └── Authorize.php
│ ├── Jobs/
│ │ └── MediaHubOptimizeAndConvertJob.php # Image optimization job
│ ├── MediaHandler/
│ │ ├── FileHandler.php # Main file handler
│ │ └── Support/
│ │ ├── Base64File.php
│ │ ├── DatePathMaker.php
│ │ ├── FileHelpers.php
│ │ ├── FileNamer.php
│ │ ├── FileValidator.php
│ │ ├── Filesystem.php
│ │ ├── MediaManipulator.php
│ │ ├── MediaOptimizer.php
│ │ ├── PathMaker.php
│ │ ├── RemoteFile.php
│ │ └── Traits/
│ │ └── PathMakerHelpers.php
│ ├── Models/
│ │ └── Media.php # Media Eloquent model
│ ├── Nova/
│ │ ├── Fields/
│ │ │ └── MediaHubField.php # Nova field
│ │ └── Resources/
│ │ └── Media.php # Nova resource
│ ├── MediaHub.php # Nova tool class
│ └── MediaHubServiceProvider.php # Service provider
├── tests/ # Test suite
│ ├── TestCase.php
│ └── Unit/
│ └── PackageTest.php
├── workbench/ # Development workbench
│ ├── app/
│ ├── bootstrap/
│ ├── database/
│ ├── resources/
│ └── routes/
├── .editorconfig # Editor configuration
├── .gitignore # Git ignore rules
├── .prettierrc # Prettier configuration
├── CHANGELOG.md # Version history
├── composer.json # PHP dependencies
├── LICENSE.md # MIT license
├── package.json # NPM dependencies
├── phpunit.xml # PHPUnit configuration
├── README.md # This file
├── tailwind.config.js # Tailwind CSS config
├── testbench.yaml # Testbench configuration
└── webpack.mix.js # Laravel Mix config
```
## Installation
Install the package via Composer:
```bash
composer require iamgerwin/nova-media-hub
```
Run the migrations to create the media library table:
```bash
php artisan migrate
```
Publish the configuration file (optional):
```bash
php artisan vendor:publish --provider="Iamgerwin\NovaMediaHub\MediaHubServiceProvider" --tag="config"
```
Publish translations (optional):
```bash
php artisan vendor:publish --provider="Iamgerwin\NovaMediaHub\MediaHubServiceProvider" --tag="translations"
```
Register the tool in your `NovaServiceProvider`:
```php
// app/Providers/NovaServiceProvider.php
use Iamgerwin\NovaMediaHub\MediaHub;
public function tools()
{
return [
MediaHub::make(),
];
}
```
## Configuration
The package configuration file is located at `config/nova-media-hub.php`. Key configuration options include:
### Storage Configuration
```php
'disk' => env('MEDIA_HUB_DISK', 'public'),
'path' => env('MEDIA_HUB_PATH', 'media'),
```
### Chunked Upload Configuration
```php
'chunked_upload' => [
// Enable/disable chunked upload feature
'enabled' => true,
// Size of each chunk (adjust based on server limits)
'chunk_size' => 5 * 1024 * 1024, // 5MB
// Maximum file size for uploads
'max_file_size' => 1024 * 1024 * 1024, // 1GB
// Upload session lifetime
'session_lifetime_hours' => 24,
// Retry attempts for failed chunks
'retry_attempts' => 3,
// Auto-enable chunked upload for files larger than this
'auto_threshold' => 10 * 1024 * 1024, // 10MB
// Cleanup old chunks after this many hours
'cleanup_old_chunks_after_hours' => 48,
],
```
### Image Optimization
```php
'image_conversions' => [
'thumbnail' => [
'width' => 150,
'height' => 150,
'fit' => 'crop',
],
'medium' => [
'width' => 800,
'height' => 600,
'fit' => 'contain',
],
],
'image_optimizer' => [
'enabled' => true,
'quality' => 85,
],
```
## Usage
### Basic Usage
Add the `MediaHubField` to your Nova resource:
```php
use Iamgerwin\NovaMediaHub\Nova\Fields\MediaHubField;
class Product extends Resource
{
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Name'),
MediaHubField::make('Image', 'image'),
];
}
}
```
### Field Configuration
#### Single Media Selection
```php
MediaHubField::make('Featured Image', 'featured_image')
->defaultCollection('featured')
->rules('required');
```
#### Multiple Media Selection
```php
MediaHubField::make('Gallery', 'gallery_images')
->multiple()
->defaultCollection('galleries')
->max(10);
```
#### Collection-specific Selection
```php
MediaHubField::make('Product Images', 'images')
->multiple()
->defaultCollection('products')
->filterCollection('products'); // Only show media from this collection
```
#### Hide from Listing
```php
MediaHubField::make('Images', 'images')
->hideFromIndex();
```
### Model Integration
#### Using Media Cast
The package provides a custom cast for seamless model integration:
```php
use Iamgerwin\NovaMediaHub\Casts\MediaCast;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $casts = [
'image' => MediaCast::class,
'gallery_images' => MediaCast::class,
];
}
```
#### Accessing Media in Blade
```blade
{{-- Single media --}}

{{-- Multiple media --}}
@foreach($product->gallery_images as $image)
@endforeach
{{-- Specific conversion --}}
```
#### Accessing Media in Controllers
```php
// Get media URL
$url = $product->image->url;
$thumbnailUrl = $product->image->url('thumbnail');
// Get media metadata
$filename = $product->image->filename;
$mimeType = $product->image->mime_type;
$size = $product->image->size;
$collection = $product->image->collection;
// Check media type
$isImage = $product->image->isImage();
$isVideo = $product->image->isVideo();
$isPdf = $product->image->isPdf();
```
### Chunked Upload API
For custom implementations or advanced use cases, you can use the `ChunkedUploader` JavaScript class:
```javascript
import { ChunkedUploader } from './utils/ChunkedUploader';
const uploader = new ChunkedUploader({
baseUrl: '/nova-vendor/media-hub/chunked',
chunkSize: 5 * 1024 * 1024, // 5MB
retryAttempts: 3,
onProgress: (chunkIndex, totalChunks, percentage) => {
console.log(`Uploading: ${percentage}%`);
updateProgressBar(percentage);
},
onComplete: (media) => {
console.log('Upload complete:', media);
displayMedia(media);
},
onError: (error) => {
console.error('Upload failed:', error);
showErrorMessage(error.message);
}
});
// Start upload
const file = document.getElementById('file-input').files[0];
await uploader.upload(file, 'my-collection', { alt: 'My Image' });
// Cancel upload
await uploader.cancel();
```
## Advanced Use Cases
### Custom Media Fields
Add custom metadata fields to the Media Hub:
```php
// app/Providers/NovaServiceProvider.php
MediaHub::make()
->withCustomFields([
'copyright' => __('Copyright'),
'photographer' => __('Photographer'),
'license' => __('License'),
]);
```
### Programmatic Media Upload
```php
use Iamgerwin\NovaMediaHub\MediaHandler\FileHandler;
$fileHandler = app(FileHandler::class);
// Upload from file path
$media = $fileHandler->fromFile('/path/to/file.jpg', 'products', [
'alt' => 'Product image',
'copyright' => '© 2025',
]);
// Upload from URL
$media = $fileHandler->fromUrl('https://example.com/image.jpg', 'external', [
'alt' => 'External image',
]);
// Upload from base64
$media = $fileHandler->fromBase64($base64Data, 'uploads', [
'alt' => 'Base64 image',
]);
```
### Batch Operations
```php
use Iamgerwin\NovaMediaHub\Models\Media;
// Move media to another collection
Media::whereCollection('old-collection')
->update(['collection' => 'new-collection']);
// Delete unused media
Media::whereDoesntHave('models')
->where('created_at', '<', now()->subMonths(6))
->delete();
// Get media by type
$images = Media::where('mime_type', 'like', 'image/%')->get();
$videos = Media::where('mime_type', 'like', 'video/%')->get();
```
### Custom Image Conversions
```php
// config/nova-media-hub.php
'image_conversions' => [
'thumbnail' => [
'width' => 150,
'height' => 150,
'fit' => 'crop',
],
'square' => [
'width' => 500,
'height' => 500,
'fit' => 'crop',
],
'hero' => [
'width' => 1920,
'height' => 1080,
'fit' => 'contain',
],
],
```
### Queue Image Processing
For better performance, queue image optimization:
```php
// config/nova-media-hub.php
'queue_conversions' => true,
'queue_name' => 'media',
```
Don't forget to run the queue worker:
```bash
php artisan queue:work --queue=media
```
## Edge Cases
### Large File Uploads (>100MB)
For very large files, adjust the chunk size and timeout settings:
```php
// config/nova-media-hub.php
'chunked_upload' => [
'chunk_size' => 10 * 1024 * 1024, // 10MB chunks
'session_lifetime_hours' => 48, // Longer session
'retry_attempts' => 5, // More retry attempts
],
```
Also, ensure your server has adequate disk space for temporary chunks.
### Slow Network Connections
The chunked upload system handles slow connections gracefully with automatic retries. Configure retry behavior:
```php
'chunked_upload' => [
'retry_attempts' => 5,
'retry_delay' => 2000, // milliseconds between retries
],
```
### Concurrent Uploads
The package supports concurrent uploads using UUID-based session management. Each upload maintains its own isolated state.
```javascript
// Upload multiple files concurrently
const uploaders = files.map(file => {
const uploader = new ChunkedUploader(config);
return uploader.upload(file, 'collection');
});
await Promise.all(uploaders);
```
### Memory-Constrained Environments
The package uses streaming for chunk combination to minimize memory usage:
```php
// Chunks are combined using stream_copy_to_stream()
// Memory usage stays constant regardless of file size
```
### Interrupted Uploads
If an upload is interrupted (browser closed, connection lost), you can resume:
```javascript
// Check for existing upload session
const uploadId = localStorage.getItem('upload-session-id');
if (uploadId) {
const status = await fetch(`/chunked/status/${uploadId}`).then(r => r.json());
if (status.chunks_uploaded < status.total_chunks) {
// Resume from last uploaded chunk
uploader.resume(uploadId, status.chunks_uploaded);
}
}
```
### Cross-Domain Uploads
When uploading from a different domain, ensure CORS is properly configured:
```php
// config/cors.php
'paths' => ['nova-vendor/media-hub/*'],
'allowed_methods' => ['POST', 'GET', 'OPTIONS'],
'allowed_origins' => ['https://your-domain.com'],
'allowed_headers' => ['Content-Type', 'X-Requested-With', 'X-CSRF-TOKEN'],
```
### File Type Restrictions
Restrict allowed file types:
```php
// config/nova-media-hub.php
'allowed_mime_types' => [
'image/jpeg',
'image/png',
'image/webp',
'image/svg+xml',
'video/mp4',
'video/webm',
'application/pdf',
],
'max_file_size' => 50 * 1024 * 1024, // 50MB
```
## Testing
The package includes a comprehensive test suite to ensure stability and reliability.
### Running Tests
```bash
# Run all tests
composer test
# Run tests with coverage
composer test-coverage
# Run specific test file
./vendor/bin/phpunit tests/Unit/PackageTest.php
# Run tests in verbose mode
./vendor/bin/phpunit --verbose
```
### Test Structure
```
tests/
├── TestCase.php # Base test case
└── Unit/
└── PackageTest.php # Package loading tests
```
### Writing Tests
When contributing, please include tests for new features:
```php
namespace Iamgerwin\NovaMediaHub\Tests\Unit;
use Iamgerwin\NovaMediaHub\Tests\TestCase;
class YourFeatureTest extends TestCase
{
/** @test */
public function it_does_something()
{
// Arrange
$input = 'test';
// Act
$result = yourFunction($input);
// Assert
$this->assertEquals('expected', $result);
}
}
```
### Continuous Integration
The package uses GitHub Actions for automated testing across multiple PHP and Laravel versions:
- **PHP Versions**: 8.2, 8.3
- **Laravel Versions**: 10.x, 11.x
- **Test Matrix**: 12+ version combinations
View test results: [GitHub Actions](https://github.com/iamgerwin/nova-media-hub/actions)
## Security
### Security Vulnerabilities
If you discover a security vulnerability within Nova Media Hub, please send an email to [iamgerwin@live.com](mailto:iamgerwin@live.com). All security vulnerabilities will be promptly addressed.
**Please do not create public GitHub issues for security vulnerabilities.**
### Security Features
The package implements several security measures:
#### File Upload Security
- **MIME Type Validation** - Validates file types before upload
- **File Size Limits** - Enforces maximum file size restrictions
- **Filename Sanitization** - Removes dangerous characters from filenames
- **Extension Whitelisting** - Only allows specified file extensions
```php
// config/nova-media-hub.php
'allowed_mime_types' => [
'image/jpeg',
'image/png',
'image/webp',
'video/mp4',
],
'max_file_size' => 50 * 1024 * 1024, // 50MB
```
#### Chunked Upload Security
- **UUID Session IDs** - Prevents session guessing attacks
- **CSRF Protection** - All endpoints require valid CSRF tokens
- **Chunk Index Validation** - Prevents malicious chunk injection
- **File Hash Verification** - Validates file integrity after assembly
- **Temporary File Isolation** - Chunks stored in isolated directories
#### Authorization
- **Nova Authorization** - Respects Nova's authorization policies
- **Middleware Protection** - All routes protected by authentication middleware
- **Permission Checks** - User permissions verified before operations
```php
// Define authorization in Nova resource
public static function authorizedToCreate(Request $request)
{
return $request->user()->can('create-media');
}
```
### Best Practices
1. **Validate User Input** - Always validate and sanitize user-provided data
2. **Restrict File Types** - Only allow necessary MIME types
3. **Set Size Limits** - Configure appropriate file size limits
4. **Use HTTPS** - Always serve media over HTTPS in production
5. **Regular Updates** - Keep the package and dependencies up to date
6. **Monitor Uploads** - Log and monitor upload activity for suspicious patterns
### Reported Vulnerabilities
None reported as of the latest release (0.0.3).
## Maintenance
### Cleanup Command
The package includes a cleanup command to remove old temporary chunk files:
```bash
# Run cleanup manually
php artisan media-hub:cleanup-chunks
# Dry run (see what would be deleted)
php artisan media-hub:cleanup-chunks --dry-run
# Custom time threshold (default: 48 hours)
php artisan media-hub:cleanup-chunks --hours=24
```
### Scheduled Cleanup
Add the cleanup command to your scheduler in `app/Console/Kernel.php`:
```php
protected function schedule(Schedule $schedule)
{
// Clean up chunks older than 48 hours, daily at 3am
$schedule->command('media-hub:cleanup-chunks')->dailyAt('03:00');
// Or use cron expression
$schedule->command('media-hub:cleanup-chunks')->cron('0 3 * * *');
}
```
### Monitoring Disk Usage
Monitor storage disk usage to prevent issues:
```bash
# Check disk usage
df -h
# Check media directory size
du -sh storage/app/public/media
# Check chunks directory size
du -sh storage/app/chunks
```
## Contributing
Contributions are welcome! Please follow these guidelines:
1. **Fork the repository**
```bash
git clone https://github.com/iamgerwin/nova-media-hub.git
cd nova-media-hub
```
2. **Create a feature branch**
```bash
git checkout -b feature/your-feature-name
```
3. **Install dependencies**
```bash
composer install
npm install
```
4. **Make your changes**
- Write tests for new features
- Follow PSR-12 coding standards
- Update documentation as needed
5. **Run tests**
```bash
composer test
./vendor/bin/pint
```
6. **Commit your changes**
```bash
git add .
git commit -m "Add feature: your feature description"
```
7. **Push to your fork**
```bash
git push origin feature/your-feature-name
```
8. **Create a Pull Request**
- Provide a clear description of the changes
- Reference any related issues
- Ensure CI tests pass
### Development Setup
```bash
# Install dependencies
composer install
# Run tests
composer test
# Run tests with coverage
composer test-coverage
# Format code
./vendor/bin/pint
# Serve development environment
composer serve
```
## Issues & Support
Found a bug or have a feature request? Please create an issue on GitHub:
**[Submit an Issue](https://github.com/iamgerwin/nova-media-hub/issues)**
When submitting an issue, please include:
- **Description** - Clear description of the issue or feature request
- **Steps to Reproduce** - Detailed steps to reproduce the issue (for bugs)
- **Expected Behavior** - What you expected to happen
- **Actual Behavior** - What actually happened
- **Environment**:
- PHP version
- Laravel version
- Nova version
- Package version
- Operating system
- **Code Samples** - Relevant code snippets or configuration
- **Screenshots** - If applicable
### Before Submitting an Issue
1. Check if the issue already exists
2. Update to the latest version
3. Check the [documentation](#table-of-contents)
4. Review [closed issues](https://github.com/iamgerwin/nova-media-hub/issues?q=is%3Aissue+is%3Aclosed) for similar problems
### Security Vulnerabilities
If you discover a security vulnerability, please email [iamgerwin@live.com](mailto:iamgerwin@live.com) instead of using the issue tracker.
## Credits
- **Author**: [John Gerwin De las Alas](https://github.com/iamgerwin)
- **Contributors**: [All Contributors](https://github.com/iamgerwin/nova-media-hub/graphs/contributors)
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
---
**Links**
- [Packagist](https://packagist.org/packages/iamgerwin/nova-media-hub)
- [GitHub Repository](https://github.com/iamgerwin/nova-media-hub)
- [Issue Tracker](https://github.com/iamgerwin/nova-media-hub/issues)
- [Changelog](CHANGELOG.md)