An open API service indexing awesome lists of open source software.

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.

Awesome Lists containing this project

README

          

# Nova Media Hub

[![Tests](https://github.com/iamgerwin/nova-media-hub/workflows/Tests/badge.svg)](https://github.com/iamgerwin/nova-media-hub/actions)
[![Latest Version](https://img.shields.io/packagist/v/iamgerwin/nova-media-hub.svg?style=flat-square)](https://packagist.org/packages/iamgerwin/nova-media-hub)
[![License](https://img.shields.io/packagist/l/iamgerwin/nova-media-hub.svg?style=flat-square)](https://packagist.org/packages/iamgerwin/nova-media-hub)
[![Total Downloads](https://img.shields.io/packagist/dt/iamgerwin/nova-media-hub.svg?style=flat-square)](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 --}}
{{ $product->image->alt }}

{{-- Multiple media --}}
@foreach($product->gallery_images as $image)
{{ $image->alt }}
@endforeach

{{-- Specific conversion --}}
{{ $product->image->alt }}
```

#### 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)