PHPackages                             iamgerwin/nova-media-hub - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Image &amp; Media](/categories/media)
4. /
5. iamgerwin/nova-media-hub

ActiveLibrary[Image &amp; Media](/categories/media)

iamgerwin/nova-media-hub
========================

Nova Media Hub with chunked upload support - Multi-version compatible (PHP 8.2-8.3, Laravel 10-12, Nova 4-5)

0.0.3(7mo ago)01[1 issues](https://github.com/iamgerwin/nova-media-hub/issues)MITPHPPHP ^8.2 || ^8.3CI passing

Since Oct 13Pushed 7mo agoCompare

[ Source](https://github.com/iamgerwin/nova-media-hub)[ Packagist](https://packagist.org/packages/iamgerwin/nova-media-hub)[ RSS](/packages/iamgerwin-nova-media-hub/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (11)Versions (2)Used By (0)

Nova Media Hub
==============

[](#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://camo.githubusercontent.com/f15caf4f9ea066bf8ab77882f1cc964b5f2ccab5852f4f29d525795820e99dc7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f69616d67657277696e2f6e6f76612d6d656469612d6875622e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/iamgerwin/nova-media-hub)[![License](https://camo.githubusercontent.com/ef51f23ce683a74c9f7e58af1eac495a86b7a2468a0d89aaf59b6097ffc7c395/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f69616d67657277696e2f6e6f76612d6d656469612d6875622e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/iamgerwin/nova-media-hub)[![Total Downloads](https://camo.githubusercontent.com/45c7a026f844e709c2dacffeed9a3aa2c10fa6d1b770a7328084d85852e8544c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f69616d67657277696e2f6e6f76612d6d656469612d6875622e7376673f7374796c653d666c61742d737175617265)](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
-----------------

[](#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 &amp; Support](#issues--support)
- [Credits](#credits)
- [License](#license)

Features
--------

[](#features)

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

[](#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

[](#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
------------

[](#requirements)

ComponentVersionPHP8.2, 8.3+Laravel10.x, 11.x, 12.xLaravel Nova4.x, 5.xPackage Structure
-----------------

[](#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
------------

[](#installation)

Install the package via Composer:

```
composer require iamgerwin/nova-media-hub
```

Run the migrations to create the media library table:

```
php artisan migrate
```

Publish the configuration file (optional):

```
php artisan vendor:publish --provider="Iamgerwin\NovaMediaHub\MediaHubServiceProvider" --tag="config"
```

Publish translations (optional):

```
php artisan vendor:publish --provider="Iamgerwin\NovaMediaHub\MediaHubServiceProvider" --tag="translations"
```

Register the tool in your `NovaServiceProvider`:

```
// app/Providers/NovaServiceProvider.php

use Iamgerwin\NovaMediaHub\MediaHub;

public function tools()
{
    return [
        MediaHub::make(),
    ];
}
```

Configuration
-------------

[](#configuration)

The package configuration file is located at `config/nova-media-hub.php`. Key configuration options include:

### Storage Configuration

[](#storage-configuration)

```
'disk' => env('MEDIA_HUB_DISK', 'public'),
'path' => env('MEDIA_HUB_PATH', 'media'),
```

### Chunked Upload Configuration

[](#chunked-upload-configuration)

```
'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

[](#image-optimization)

```
'image_conversions' => [
    'thumbnail' => [
        'width' => 150,
        'height' => 150,
        'fit' => 'crop',
    ],
    'medium' => [
        'width' => 800,
        'height' => 600,
        'fit' => 'contain',
    ],
],

'image_optimizer' => [
    'enabled' => true,
    'quality' => 85,
],
```

Usage
-----

[](#usage)

### Basic Usage

[](#basic-usage)

Add the `MediaHubField` to your Nova resource:

```
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

[](#field-configuration)

#### Single Media Selection

[](#single-media-selection)

```
MediaHubField::make('Featured Image', 'featured_image')
    ->defaultCollection('featured')
    ->rules('required');
```

#### Multiple Media Selection

[](#multiple-media-selection)

```
MediaHubField::make('Gallery', 'gallery_images')
    ->multiple()
    ->defaultCollection('galleries')
    ->max(10);
```

#### Collection-specific Selection

[](#collection-specific-selection)

```
MediaHubField::make('Product Images', 'images')
    ->multiple()
    ->defaultCollection('products')
    ->filterCollection('products'); // Only show media from this collection
```

#### Hide from Listing

[](#hide-from-listing)

```
MediaHubField::make('Images', 'images')
    ->hideFromIndex();
```

### Model Integration

[](#model-integration)

#### Using Media Cast

[](#using-media-cast)

The package provides a custom cast for seamless model integration:

```
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

[](#accessing-media-in-blade)

```
{{-- Single media --}}

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

@endforeach

{{-- Specific conversion --}}

```

#### Accessing Media in Controllers

[](#accessing-media-in-controllers)

```
// 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

[](#chunked-upload-api)

For custom implementations or advanced use cases, you can use the `ChunkedUploader` JavaScript class:

```
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
------------------

[](#advanced-use-cases)

### Custom Media Fields

[](#custom-media-fields)

Add custom metadata fields to the Media Hub:

```
// app/Providers/NovaServiceProvider.php

MediaHub::make()
    ->withCustomFields([
        'copyright' => __('Copyright'),
        'photographer' => __('Photographer'),
        'license' => __('License'),
    ]);
```

### Programmatic Media Upload

[](#programmatic-media-upload)

```
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

[](#batch-operations)

```
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', ' [
    '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

[](#slow-network-connections)

The chunked upload system handles slow connections gracefully with automatic retries. Configure retry behavior:

```
'chunked_upload' => [
    'retry_attempts' => 5,
    'retry_delay' => 2000, // milliseconds between retries
],
```

### Concurrent Uploads

[](#concurrent-uploads)

The package supports concurrent uploads using UUID-based session management. Each upload maintains its own isolated state.

```
// 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

[](#memory-constrained-environments)

The package uses streaming for chunk combination to minimize memory usage:

```
// Chunks are combined using stream_copy_to_stream()
// Memory usage stays constant regardless of file size
```

### Interrupted Uploads

[](#interrupted-uploads)

If an upload is interrupted (browser closed, connection lost), you can resume:

```
// 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  ['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

[](#file-type-restrictions)

Restrict allowed file types:

```
// 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
-------

[](#testing)

The package includes a comprehensive test suite to ensure stability and reliability.

### Running Tests

[](#running-tests)

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

[](#test-structure)

```
tests/
├── TestCase.php              # Base test case
└── Unit/
    └── PackageTest.php       # Package loading tests

```

### Writing Tests

[](#writing-tests)

When contributing, please include tests for new features:

```
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

[](#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)

### Security Vulnerabilities

[](#security-vulnerabilities)

If you discover a security vulnerability within Nova Media Hub, please send an email to . All security vulnerabilities will be promptly addressed.

**Please do not create public GitHub issues for security vulnerabilities.**

### Security Features

[](#security-features)

The package implements several security measures:

#### File Upload Security

[](#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

```
// 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

[](#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

[](#authorization)

- **Nova Authorization** - Respects Nova's authorization policies
- **Middleware Protection** - All routes protected by authentication middleware
- **Permission Checks** - User permissions verified before operations

```
// Define authorization in Nova resource
public static function authorizedToCreate(Request $request)
{
    return $request->user()->can('create-media');
}
```

### Best Practices

[](#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

[](#reported-vulnerabilities)

None reported as of the latest release (0.0.3).

Maintenance
-----------

[](#maintenance)

### Cleanup Command

[](#cleanup-command)

The package includes a cleanup command to remove old temporary chunk files:

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

[](#scheduled-cleanup)

Add the cleanup command to your scheduler in `app/Console/Kernel.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

[](#monitoring-disk-usage)

Monitor storage disk usage to prevent issues:

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

[](#contributing)

Contributions are welcome! Please follow these guidelines:

1. **Fork the repository**

    ```
    git clone https://github.com/iamgerwin/nova-media-hub.git
    cd nova-media-hub
    ```
2. **Create a feature branch**

    ```
    git checkout -b feature/your-feature-name
    ```
3. **Install dependencies**

    ```
    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**

    ```
    composer test
    ./vendor/bin/pint
    ```
6. **Commit your changes**

    ```
    git add .
    git commit -m "Add feature: your feature description"
    ```
7. **Push to your fork**

    ```
    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

[](#development-setup)

```
# 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 &amp; Support
--------------------

[](#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

[](#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

[](#security-vulnerabilities-1)

If you discover a security vulnerability, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- **Author**: [John Gerwin De las Alas](https://github.com/iamgerwin)
- **Contributors**: [All Contributors](https://github.com/iamgerwin/nova-media-hub/graphs/contributors)

License
-------

[](#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)

###  Health Score

29

—

LowBetter than 60% of packages

Maintenance65

Regular maintenance activity

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

210d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8f6f1736b8e2833ccb6c4098e4ecb5081d9cd5db2af370fde0365d441fbfbf59?d=identicon)[iamgerwin](/maintainers/iamgerwin)

---

Top Contributors

[![iamgerwin](https://avatars.githubusercontent.com/u/1331683?v=4)](https://github.com/iamgerwin "iamgerwin (23 commits)")

---

Tags

filemanagermediamediahubnovalaravelmediachunked-uploadnovalarge files

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/iamgerwin-nova-media-hub/health.svg)

```
[![Health](https://phpackages.com/badges/iamgerwin-nova-media-hub/health.svg)](https://phpackages.com/packages/iamgerwin-nova-media-hub)
```

###  Alternatives

[classic-o/nova-media-library

Tool and field that will let you managing files and add them to the posts

154172.0k](/packages/classic-o-nova-media-library)[mostafaznv/nova-ckeditor

CkEditor for Laravel Nova

57327.2k1](/packages/mostafaznv-nova-ckeditor)[silvanite/nova-field-cloudinary

A Laravel Nova Image Field with Flysystem Adapter for storing and retrieving media from Cloudinary

2972.0k3](/packages/silvanite-nova-field-cloudinary)[mostafaznv/nova-video

Video Field for Laravel Nova

22398.0k1](/packages/mostafaznv-nova-video)[outl1ne/nova-media-hub

A Laravel Nova tool for managing media.

4652.0k](/packages/outl1ne-nova-media-hub)[sbine/route-viewer

A Laravel Nova tool to view your registered routes.

57215.9k](/packages/sbine-route-viewer)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
