A comprehensive Laravel package for seamless integration with Nimble Streamer API. Manage streams, DVR, sessions, restreaming, and more with a clean, expressive Laravel interface.
Developed by Alex Hackney
- ✅ Stream Management: Create, configure, publish/unpublish, and monitor streams
- ✅ Session Management: Monitor and control active client sessions
- ✅ DVR Control: Manage archives, recordings, and playback
- ✅ Restreaming: Configure and manage restream targets
- ✅ Stream Pulling: Pull streams from external sources
- ✅ Server Management: Monitor server status, reload configuration, and sync
- ✅ Cache Control: Manage server cache and statistics
- 🚧 Icecast Integration: Manage Icecast server and inject metadata (coming soon)
- 🚧 Playlist Management: Create and control server playlists (coming soon)
- ✅ Real-Time Statistics: Get live stream stats including bandwidth, resolution, codecs, and more
- ✅ Protocol Support: RTMP, MPEG-TS, SRT, NDI, HLS, RTSP
- ✅ Test-Driven Development: Comprehensive test coverage with 127 tests
- ✅ Type Safety: Full PHP 8.1+ type hints and enums
- ✅ Laravel Integration: Service provider, facade, and configuration
- PHP 8.1 or higher
- Laravel 10 or 11
- Nimble Streamer with API enabled
- Composer
composer require alexhackney/lara-nimble
php artisan vendor:publish --provider="AlexHackney\LaraNimble\NimbleServiceProvider" --tag="nimble-config"
This will create a config/nimble.php
file in your Laravel application.
Minimal Configuration - Add only what you need to your .env
file:
# Required: Your Nimble server hostname
NIMBLE_HOST=your-nimble-server.com
# Optional: Only if your Nimble server requires authentication
NIMBLE_TOKEN=your-secret-token
That's it! The package uses sensible defaults for everything else.
Optional Overrides - Only add these if you need to change the defaults:
# Connection (defaults shown)
NIMBLE_PORT=8082 # Default Nimble management port
NIMBLE_PROTOCOL=http # Use 'https' for production
# Timeouts (in seconds)
NIMBLE_TIMEOUT=30 # Request timeout
NIMBLE_CONNECT_TIMEOUT=10 # Connection timeout
# Retry Logic
NIMBLE_RETRY_TIMES=3 # Number of retry attempts
NIMBLE_RETRY_SLEEP=100 # Milliseconds between retries
# Debug/Logging
NIMBLE_LOG_REQUESTS=false # Enable to log all requests
NIMBLE_LOG_CHANNEL=stack # Laravel log channel
# SSL (for self-signed certs in development only)
NIMBLE_VERIFY_SSL=true # Set to false to disable SSL verification
Ensure your Nimble server has the API enabled. Edit /etc/nimble/nimble.conf
:
management_port = 8082;
management_token = your-secret-token; # Optional but recommended
Restart Nimble after configuration changes:
sudo systemctl restart nimble
use AlexHackney\LaraNimble\Facades\Nimble;
// List all streams
$streams = Nimble::streams()->list();
foreach ($streams as $stream) {
echo "Stream: {$stream->name} ({$stream->status->value})\n";
echo "Protocol: {$stream->protocol->value}\n";
echo "Viewers: {$stream->viewers}\n";
}
// Get a specific stream
$stream = Nimble::streams()->get('stream-123');
echo "Status: {$stream->status->value}";
// Publish a stream
if (Nimble::streams()->publish('live', 'my-stream')) {
echo "Stream published successfully!";
} else {
echo "Failed to publish stream";
}
// Unpublish a stream
if (Nimble::streams()->unpublish('live', 'my-stream')) {
echo "Stream unpublished successfully!";
}
// Get stream statistics
$stats = Nimble::streams()->statistics('stream-123');
echo "Bitrate: {$stats['bitrate']} kbps\n";
echo "Viewers: {$stats['viewers']}\n";
echo "Duration: {$stats['duration']} seconds\n";
Get comprehensive, real-time statistics for active streams including bandwidth, resolution, codecs, protocol information, and publisher details. Perfect for monitoring dashboards and live stream health checks.
use AlexHackney\LaraNimble\Facades\Nimble;
// Get real-time stats for a specific stream by stream key
$stats = Nimble::streams()->liveStatus('my-stream-key');
if ($stats) {
echo "Stream: {$stats->streamName}\n";
echo "Application: {$stats->application}\n";
echo "Protocol: {$stats->protocol}\n";
echo "Resolution: {$stats->resolution}\n";
echo "Video Codec: {$stats->videoCodec}\n";
echo "Audio Codec: {$stats->audioCodec}\n";
echo "Bandwidth: " . ($stats->bandwidth / 1000000) . " Mbps\n";
echo "Bitrate: {$stats->bitrate} kbps\n";
echo "FPS: {$stats->fps}\n";
echo "Viewers: {$stats->viewers}\n";
echo "Duration: {$stats->duration} seconds\n";
echo "Publisher IP: {$stats->publisherIp}\n";
echo "Publisher Port: {$stats->publisherPort}\n";
echo "Source URL: {$stats->sourceUrl}\n";
echo "Started: {$stats->startTime}\n";
} else {
echo "Stream is not currently live\n";
}
// Get real-time stats for ALL currently active streams
$allStreams = Nimble::streams()->allLiveStreams();
foreach ($allStreams as $stream) {
echo "Stream: {$stream->streamName}\n";
echo " Protocol: {$stream->protocol}\n";
echo " Resolution: {$stream->resolution}\n";
echo " Bandwidth: " . ($stream->bandwidth / 1000000) . " Mbps\n";
echo " Viewers: {$stream->viewers}\n";
echo "\n";
}
// Example: Build a real-time monitoring dashboard
$liveStreams = Nimble::streams()->allLiveStreams();
$dashboard = $liveStreams->map(function ($stream) {
return [
'name' => $stream->streamName,
'app' => $stream->application,
'status' => 'live',
'protocol' => $stream->protocol,
'quality' => $stream->resolution,
'codecs' => [
'video' => $stream->videoCodec,
'audio' => $stream->audioCodec,
],
'network' => [
'bandwidth_mbps' => round($stream->bandwidth / 1000000, 2),
'bitrate_kbps' => $stream->bitrate,
'publisher_ip' => $stream->publisherIp,
],
'metrics' => [
'viewers' => $stream->viewers,
'fps' => $stream->fps,
'uptime_seconds' => $stream->duration,
],
];
});
return response()->json(['streams' => $dashboard]);
Use Cases for Real-Time Statistics:
- Live Monitoring: Display current stream health in admin dashboards
- Quality Alerts: Detect bitrate drops or resolution changes
- Viewer Analytics: Track real-time viewer counts
- Stream Discovery: List all currently active streams
- Protocol Monitoring: Track which protocols are being used (RTMP, SRT, NDI)
- Network Analysis: Monitor bandwidth usage and publisher connections
use AlexHackney\LaraNimble\Facades\Nimble;
// List all active sessions
$sessions = Nimble::sessions()->list();
foreach ($sessions as $session) {
echo "Session: {$session->id}\n";
echo "Client IP: {$session->clientIp}\n";
echo "Protocol: {$session->protocol}\n";
echo "Duration: {$session->duration} seconds\n";
}
// Get a specific session
$session = Nimble::sessions()->get('session-456');
echo "Stream: {$session->streamId}";
// Terminate a session
if (Nimble::sessions()->terminate('session-456')) {
echo "Session terminated successfully!";
}
// Get session statistics
$stats = Nimble::sessions()->statistics('session-456');
echo "Bytes transferred: {$stats['bytes_transferred']}\n";
use AlexHackney\LaraNimble\Facades\Nimble;
// List all DVR archives
$archives = Nimble::dvr()->listArchives();
foreach ($archives as $archive) {
echo "Archive: {$archive->filename}\n";
echo "Stream: {$archive->streamId}\n";
echo "Size: {$archive->size} bytes\n";
echo "Duration: {$archive->duration} seconds\n";
}
// Get a specific archive
$archive = Nimble::dvr()->getArchive('archive-123');
echo "Path: {$archive->path}";
// Delete an archive
if (Nimble::dvr()->deleteArchive('archive-123')) {
echo "Archive deleted successfully!";
}
// Configure DVR settings
if (Nimble::dvr()->configure([
'stream' => 'stream1',
'enabled' => true,
'path' => '/var/dvr/stream1',
'max_duration' => 3600,
])) {
echo "DVR configured successfully!";
}
use AlexHackney\LaraNimble\Facades\Nimble;
// List all restream targets
$restreams = Nimble::restream()->list();
foreach ($restreams as $restream) {
echo "Target: {$restream->targetUrl}\n";
echo "Protocol: {$restream->protocol}\n";
echo "Status: {$restream->status}\n";
echo "Enabled: " . ($restream->enabled ? 'Yes' : 'No') . "\n";
}
// Get a specific restream target
$restream = Nimble::restream()->get('restream-123');
echo "Target URL: {$restream->targetUrl}";
// Add a new restream target
if (Nimble::restream()->add('stream-123', [
'target_url' => 'rtmp://live.youtube.com/stream/key123',
'protocol' => 'rtmp',
'enabled' => true,
])) {
echo "Restream target added successfully!";
}
// Update a restream target
if (Nimble::restream()->update('restream-123', [
'enabled' => false,
])) {
echo "Restream target updated!";
}
// Delete a restream target
if (Nimble::restream()->delete('restream-123')) {
echo "Restream target deleted!";
}
use AlexHackney\LaraNimble\Facades\Nimble;
// List all pull configurations
$pulls = Nimble::pull()->list();
foreach ($pulls as $pull) {
echo "Source: {$pull->sourceUrl}\n";
echo "Local: {$pull->localApp}/{$pull->localStream}\n";
echo "Protocol: {$pull->protocol}\n";
echo "Status: {$pull->status}\n";
}
// Get a specific pull configuration
$pull = Nimble::pull()->get('pull-123');
echo "Source URL: {$pull->sourceUrl}";
// Add a new pull configuration
if (Nimble::pull()->add([
'source_url' => 'rtmp://source.com/live/stream',
'local_app' => 'live',
'local_stream' => 'pulled-stream',
'protocol' => 'rtmp',
'enabled' => true,
])) {
echo "Pull configuration added successfully!";
}
// Update a pull configuration
if (Nimble::pull()->update('pull-123', [
'enabled' => false,
])) {
echo "Pull configuration updated!";
}
// Delete a pull configuration
if (Nimble::pull()->delete('pull-123')) {
echo "Pull configuration deleted!";
}
// Get pull stream status
$status = Nimble::pull()->status('pull-123');
echo "Uptime: {$status['uptime']} seconds\n";
echo "Bitrate: {$status['bitrate']} kbps\n";
use AlexHackney\LaraNimble\Facades\Nimble;
// Get server status
$status = Nimble::server()->status();
echo "Status: {$status->status}\n";
echo "Version: {$status->version}\n";
echo "Uptime: {$status->uptime} seconds\n";
echo "Connections: {$status->connections}\n";
echo "Bandwidth In: {$status->bandwidth['in']} bytes\n";
echo "Bandwidth Out: {$status->bandwidth['out']} bytes\n";
// Reload server configuration
if (Nimble::server()->reload()) {
echo "Server configuration reloaded successfully!";
}
// Sync with WMSPanel
if (Nimble::server()->sync()) {
echo "Synchronization completed!";
}
use AlexHackney\LaraNimble\Facades\Nimble;
// Clear all cache
if (Nimble::cache()->clear()) {
echo "Cache cleared successfully!";
}
// Clear specific cache type
if (Nimble::cache()->clear('hls')) {
echo "HLS cache cleared!";
}
// Get cache statistics
$stats = Nimble::cache()->statistics();
echo "Total Size: {$stats['total_size']} bytes\n";
echo "Items: {$stats['items']}\n";
echo "Hit Rate: {$stats['hit_rate']}\n";
echo "Miss Rate: {$stats['miss_rate']}\n";
// Configure cache settings
if (Nimble::cache()->configure([
'enabled' => true,
'max_size' => 2147483648,
'ttl' => 3600,
])) {
echo "Cache configured successfully!";
}
<?php
namespace App\Http\Controllers;
use AlexHackney\LaraNimble\Facades\Nimble;
use Illuminate\Http\JsonResponse;
class StreamController extends Controller
{
public function index(): JsonResponse
{
$streams = Nimble::streams()->list();
return response()->json([
'streams' => $streams->map->toArray(),
]);
}
public function publish(string $app, string $stream): JsonResponse
{
$success = Nimble::streams()->publish($app, $stream);
return response()->json([
'success' => $success,
'message' => $success ? 'Stream published' : 'Failed to publish stream',
]);
}
public function unpublish(string $app, string $stream): JsonResponse
{
$success = Nimble::streams()->unpublish($app, $stream);
return response()->json([
'success' => $success,
'message' => $success ? 'Stream unpublished' : 'Failed to unpublish stream',
]);
}
public function sessions(): JsonResponse
{
$sessions = Nimble::sessions()->list();
return response()->json([
'sessions' => $sessions->map->toArray(),
'count' => $sessions->count(),
]);
}
}
<?php
namespace App\Jobs;
use AlexHackney\LaraNimble\Facades\Nimble;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class PublishStreamJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
public string $app,
public string $stream
) {
}
public function handle(): void
{
$success = Nimble::streams()->publish($this->app, $this->stream);
if (!$success) {
$this->fail('Failed to publish stream');
}
}
}
The package is designed for minimal configuration. Only two settings are required in your .env
:
NIMBLE_HOST=your-server.com # Required
NIMBLE_TOKEN=your-token # Optional, only if server requires auth
All other settings have sensible defaults that work for most use cases. The config/nimble.php
file shows all available options with their defaults:
return [
// Connection (only NIMBLE_HOST is required)
'host' => env('NIMBLE_HOST', 'localhost'), // Your Nimble server
'port' => env('NIMBLE_PORT', 8082), // Standard Nimble API port
'protocol' => env('NIMBLE_PROTOCOL', 'http'), // http or https
// Authentication (optional)
'token' => env('NIMBLE_TOKEN'), // Only if server requires it
// Request settings (rarely need changing)
'timeout' => env('NIMBLE_TIMEOUT', 30), // Seconds
'connect_timeout' => env('NIMBLE_CONNECT_TIMEOUT', 10),
'retry_times' => env('NIMBLE_RETRY_TIMES', 3), // Auto-retry failed requests
'retry_sleep' => env('NIMBLE_RETRY_SLEEP', 100), // Milliseconds
// Logging (for debugging)
'log_requests' => env('NIMBLE_LOG_REQUESTS', false),
'log_channel' => env('NIMBLE_LOG_CHANNEL', 'stack'),
// SSL (dev only - never disable in production)
'verify_ssl' => env('NIMBLE_VERIFY_SSL', true),
];
You only need to override these in .env
if the defaults don't work for your setup.
The package includes comprehensive tests built with Test-Driven Development (TDD):
# Run all tests
composer test
# Run with coverage
composer test-coverage
# Run specific test suite
vendor/bin/phpunit tests/Unit/Services/StreamServiceTest.php
Current Test Coverage:
- 127 tests
- 330 assertions
- Unit tests for all services, DTOs, and HTTP client
- Feature tests for Laravel integration
If you're getting connection errors:
- Verify Nimble is running:
sudo systemctl status nimble
- Check firewall allows port 8082
- Verify
NIMBLE_HOST
andNIMBLE_PORT
in.env
- Test API manually:
curl http://your-server:8082/manage/status
If you're getting 401/403 errors:
- Verify
management_token
in/etc/nimble/nimble.conf
matches yourNIMBLE_TOKEN
- If not using authentication, remove
NIMBLE_TOKEN
from.env
- Restart Nimble after config changes
For development/testing with self-signed certificates:
NIMBLE_VERIFY_SSL=false
Note: Never disable SSL verification in production!
For detailed Nimble API documentation, visit:
lara-nimble/
├── src/
│ ├── Client/ # HTTP client & authentication
│ ├── DTOs/ # Data Transfer Objects
│ ├── Enums/ # Enums (protocols, statuses)
│ ├── Exceptions/ # Custom exceptions
│ ├── Facades/ # Laravel facades
│ ├── Services/ # Service classes
│ ├── Nimble.php # Main manager class
│ └── NimbleServiceProvider.php
├── tests/
│ ├── Feature/ # Laravel integration tests
│ └── Unit/ # Unit tests
└── config/
└── nimble.php # Package configuration
This package is currently in active development. Contributions welcome!
- Follow PSR-12 coding standards
- Write tests for new features (TDD approach)
- Update documentation
- Ensure all tests pass:
composer test
See CHANGELOG.md for version history.
The MIT License (MIT). See LICENSE for details.
Developed by: Alex Hackney
Built with:
- Laravel 10 & 11
- PHP 8.1+
- Nimble Streamer API
The package provides helpful Artisan commands for common operations:
php artisan nimble:status
Displays server status, version, uptime, connections, and bandwidth statistics.
php artisan nimble:streams
# Filter by status
php artisan nimble:streams --filter=active
php artisan nimble:streams --filter=inactive
Lists all streams with their status, protocol, viewers, and bitrate.
php artisan nimble:cache:clear
# Clear specific cache type
php artisan nimble:cache:clear --type=hls
php artisan nimble:cache:clear --type=dash
Clears Nimble server cache.
php artisan nimble:health
Performs a comprehensive health check of your Nimble server configuration.
The package includes custom validation rules for Laravel forms:
use AlexHackney\LaraNimble\Rules\NimbleHostRule;
use AlexHackney\LaraNimble\Rules\StreamProtocolRule;
use AlexHackney\LaraNimble\Rules\StreamExistsRule;
// Validate Nimble host connectivity
$request->validate([
'host' => ['required', new NimbleHostRule],
]);
// Validate streaming protocol
$request->validate([
'protocol' => ['required', new StreamProtocolRule],
]);
// Validate stream exists
$request->validate([
'stream_id' => ['required', new StreamExistsRule],
]);
The package dispatches events for key actions, allowing you to hook into important moments:
AlexHackney\LaraNimble\Events\StreamPublished
- Fired when a stream is publishedAlexHackney\LaraNimble\Events\StreamUnpublished
- Fired when a stream is unpublishedAlexHackney\LaraNimble\Events\SessionTerminated
- Fired when a session is terminatedAlexHackney\LaraNimble\Events\CacheCleared
- Fired when cache is cleared
Create an event listener:
<?php
namespace App\Listeners;
use AlexHackney\LaraNimble\Events\StreamPublished;
use Illuminate\Support\Facades\Log;
class LogStreamPublished
{
public function handle(StreamPublished $event): void
{
Log::info('Stream published', [
'app' => $event->app,
'stream' => $event->stream,
]);
// Send notification, update database, etc.
}
}
Register in EventServiceProvider
:
protected $listen = [
\AlexHackney\LaraNimble\Events\StreamPublished::class => [
\App\Listeners\LogStreamPublished::class,
],
];
The package includes several performance optimizations:
- Service Caching: Service instances are cached within the Nimble manager to reduce memory usage
- Connection Pooling: HTTP connections are reused when possible
- Retry Logic: Automatic retry with exponential backoff for failed requests
- Lazy Loading: Services are only instantiated when needed
We welcome contributions from the community! Here's how you can help:
If you find a bug or have a feature request, please:
- Check if the issue already exists in GitHub Issues
- If not, create a new issue with:
- Clear description of the problem or feature
- Steps to reproduce (for bugs)
- Laravel and PHP versions
- Nimble Streamer version
- Any relevant code samples or error messages
We love pull requests! To contribute code:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Follow PSR-12 coding standards
- Write tests for new features (we use TDD!)
- Ensure all tests pass:
composer test
- Run code formatting:
composer format
- Run static analysis:
composer analyse
- Commit your changes (
git commit -m 'Add amazing feature'
) - Push to your branch (
git push origin feature/amazing-feature
) - Open a Pull Request
- Test-Driven Development: Write tests first, then implement features
- Type Safety: Use strict types and full type hints
- Documentation: Update README and inline docs for new features
- Backward Compatibility: Don't break existing APIs without major version bump
- Code Style: Follow PSR-12, use Laravel conventions
# Run all tests
composer test
# Run with coverage
composer test-coverage
# Run static analysis
composer analyse
# Format code
composer format
For issues and questions:
- GitHub Issues: Report an issue
- Source Code: View on GitHub
- Documentation: README
- Email: [email protected]
See CHANGELOG.md for version history and release notes.
If you discover a security vulnerability, please email [email protected] instead of using the issue tracker. All security vulnerabilities will be promptly addressed.
Made with ❤️ using Test-Driven Development
Repository: https://github.com/alexhackney/lara-nimble