PHPackages                             alexhackney/lara-nimble - 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. [API Development](/categories/api)
4. /
5. alexhackney/lara-nimble

ActiveLibrary[API Development](/categories/api)

alexhackney/lara-nimble
=======================

A comprehensive Laravel package for seamless integration with Nimble Streamer API. Manage streams, DVR, sessions, restreaming, and more with a clean, expressive Laravel interface.

v1.0.0(2w ago)311[3 PRs](https://github.com/alexhackney/lara-nimble/pulls)MITPHPPHP ^8.3|^8.4|^8.5CI passing

Since Oct 10Pushed 2w agoCompare

[ Source](https://github.com/alexhackney/lara-nimble)[ Packagist](https://packagist.org/packages/alexhackney/lara-nimble)[ Docs](https://github.com/alexhackney/lara-nimble)[ RSS](/packages/alexhackney-lara-nimble/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (5)Dependencies (18)Versions (8)Used By (0)

Laravel Nimble Package
======================

[](#laravel-nimble-package)

A comprehensive Laravel package for the [Nimble Streamer](https://softvelum.com/nimble/) native management API. Inspect live streams and sessions, manage RTMP republishing, DVR archives, publish control, the data cache and more with a clean, expressive Laravel interface.

**Developed by Alex Hackney**

[![Latest Version on Packagist](https://camo.githubusercontent.com/686d47685f3376918abb3c3582aa9a42010b4261dedf6b4121679af9a53e178c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616c65786861636b6e65792f6c6172612d6e696d626c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/alexhackney/lara-nimble)[![Tests](https://camo.githubusercontent.com/50f92cf000298ce4a74a2707ec57cf75361e15f2ee252ff53f9f1b8d9282e992/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f616c65786861636b6e65792f6c6172612d6e696d626c652f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/alexhackney/lara-nimble/actions/workflows/tests.yml)[![Coverage](https://camo.githubusercontent.com/eaa9a323289a723a1e04f436b1aeef1420d305dfe37132f4714df0e609327676/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f616c65786861636b6e65792f6c6172612d6e696d626c653f7374796c653d666c61742d737175617265)](https://codecov.io/gh/alexhackney/lara-nimble)[![Total Downloads](https://camo.githubusercontent.com/185ffc764d9e7c0415f7a9de7a71613ac55b6bbdeb531dba8f7539752ddf8580/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f616c65786861636b6e65792f6c6172612d6e696d626c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/alexhackney/lara-nimble)[![License](https://camo.githubusercontent.com/de51a04e1aa10c2d64ede1a7f926451ec10dd204c24cee7fde82ef594c5cf74d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f616c65786861636b6e65792f6c6172612d6e696d626c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/alexhackney/lara-nimble)![PHP Version](https://camo.githubusercontent.com/fe53cf5e3d769f16a5feb66829bc25dbb0b97adb6c6b1b804a0ce10520bf7b17/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d382e33253230253743253230382e34253230253743253230382e352d626c75653f7374796c653d666c61742d737175617265)![Laravel Version](https://camo.githubusercontent.com/260dfdcba6ce9ae42ab3d6b66859364c20c95fc3d5b6ae89a4ee04c953580b90/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d313225323025374325323031332d7265643f7374796c653d666c61742d737175617265)

Every service maps 1:1 to endpoints documented in the [Nimble native API reference](https://softvelum.com/nimble/api/) — nothing is invented, and every request/response shape is covered by tests.

Features
--------

[](#features)

- ✅ **Live Streams**: Inspect currently publishing streams (bandwidth, resolution, codecs, publisher)
- ✅ **RTMP Republishing**: Create, list, delete and monitor republish rules (YouTube, Facebook, Twitch, ...)
- ✅ **Restream Reconciler**: `sync()` converges the server onto your desired rule set — built for Nimble's non-persistent API rules
- ✅ **First-class Testing**: `Nimble::fake()` with request recording and assertions
- ✅ **Session Management**: List and terminate active client sessions
- ✅ **DVR Control**: Archive status, MP4 export, reload and cleanup
- ✅ **Publish Control**: List active publishers and deny (disconnect) them
- ✅ **Server Management**: Status, config reload, SSL certificate reload, WMSPanel sync, playlist status
- ✅ **Data Cache**: Resolve cache keys and evict cached items
- ✅ **Protocol Insights**: RTMP settings, MPEG-TS status/settings, SRT/RIST stats, NDI list
- ✅ **Icecast**: Read stream info and inject metadata
- ✅ **SCTE-35**: Insert cue-out/cue-in/time\_signal ad markers (Nimble Advertizer)
- ✅ **Type Safety**: Strict types, readonly DTOs, PHP 8.3+
- ✅ **Laravel Integration**: Service provider, facade, config, artisan commands, events, validation rules

Requirements
------------

[](#requirements)

- PHP 8.3, 8.4 or 8.5
- Laravel 12 or 13
- Nimble Streamer with the management API enabled

Installation
------------

[](#installation)

### Step 1: Install via Composer

[](#step-1-install-via-composer)

```
composer require alexhackney/lara-nimble
```

### Step 2: Publish Configuration

[](#step-2-publish-configuration)

```
php artisan vendor:publish --provider="AlexHackney\LaraNimble\NimbleServiceProvider" --tag="nimble-config"
```

This will create a `config/nimble.php` file in your Laravel application.

### Step 3: Configure Environment Variables

[](#step-3-configure-environment-variables)

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

### Step 4: Enable Nimble API

[](#step-4-enable-nimble-api)

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

Usage
-----

[](#usage)

### Live Streams

[](#live-streams)

Nimble's native API exposes *currently publishing* streams via `/manage/live_streams_status`. A stream that is not live simply does not appear.

```
use AlexHackney\LaraNimble\Facades\Nimble;

// List all live streams across all applications
$streams = Nimble::streams()->list();
foreach ($streams as $stream) {
    echo "{$stream->app}/{$stream->stream}\n";
    echo "  Protocol: {$stream->protocol}\n";
    echo "  Resolution: {$stream->resolution}\n";
    echo "  Codecs: {$stream->vcodec} / {$stream->acodec}\n";
    echo "  Bandwidth: " . round(($stream->bandwidth ?? 0) / 1_000_000, 2) . " Mbps\n";
    echo "  Publisher: {$stream->publisherIp}:{$stream->publisherPort}\n";
}

// Only one application
$streams = Nimble::streams()->byApp('live');

// Find one stream (null when it is not live)
$stream = Nimble::streams()->find('live', 'stream1');

// Convenience boolean
if (Nimble::streams()->exists('live', 'stream1')) {
    echo 'Stream is live!';
}
```

### Restream Management (RTMP Republishing)

[](#restream-management-rtmp-republishing)

Restreaming uses Nimble's native RTMP republishing API (`/manage/rtmp/republish`).

```
use AlexHackney\LaraNimble\DTOs\RestreamDto;
use AlexHackney\LaraNimble\Facades\Nimble;

// List republishing rules created through this API
$rules = Nimble::restream()->list();
foreach ($rules as $rule) {
    echo "Rule {$rule->id}: {$rule->srcApp}/{$rule->srcStream}";
    echo " -> {$rule->destAddr}:{$rule->destPort}/{$rule->destApp}/{$rule->destStream}\n";
}

// Get a specific rule (null when it does not exist)
$rule = Nimble::restream()->get(42);

// Create a rule from explicit fields
$created = Nimble::restream()->create(new RestreamDto(
    srcApp: 'live',
    srcStream: 'stream1',
    destAddr: 'a.rtmp.youtube.com',
    destPort: 1935,
    destApp: 'live2',
    destStream: 'your-stream-key',
));
echo "Created rule {$created->id}";

// Or decompose an RTMP(S) publishing URL, e.g. Facebook's secure_stream_url
$created = Nimble::restream()->create(RestreamDto::fromUrl(
    'live',
    'stream1',
    'rtmps://live-api-s.facebook.com:443/rtmp/your-stream-key'
));

// Delete a rule
if (Nimble::restream()->delete(42)) {
    echo "Rule deleted!";
}

// Connection statistics for all rules
foreach (Nimble::restream()->stats() as $stat) {
    echo "Rule {$stat->id}: {$stat->state}, {$stat->bandwidth} bandwidth\n";
}
```

#### Reconciling rules with sync()

[](#reconciling-rules-with-sync)

Because API-created rules disappear on Nimble reloads, the typical pattern is a reconciler: declare the rules you want, and let `sync()` converge the server. Rules are compared by fingerprint (every field except `id`); missing rules are created, unwanted or duplicate rules deleted, matching rules left untouched. WMSPanel-defined rules are never affected because the native API cannot see them.

```
use AlexHackney\LaraNimble\DTOs\RestreamDto;
use AlexHackney\LaraNimble\Facades\Nimble;

$desired = [
    RestreamDto::fromUrl('live', 'stream1', 'rtmps://live-api-s.facebook.com:443/rtmp/fb-key'),
    RestreamDto::fromUrl('live', 'stream1', 'rtmp://a.rtmp.youtube.com/live2/yt-key'),
];

$result = Nimble::restream()->sync($desired);

if ($result->changed()) {
    logger()->info('Restream rules reconciled', [
        'created' => $result->created->count(),
        'deleted' => $result->deleted->count(),
        'kept' => $result->kept->count(),
    ]);
}
```

Run it on deploy, on a schedule, or after `Nimble::server()->reloadConfig()` — it is idempotent. `RestreamRuleCreated` / `RestreamRuleDeleted` events fire for every change, so you get an audit trail for free.

Notes from the Nimble API docs:

- `src_stream` is optional in the raw API, but omitting it republishes **every** stream in the source application, so `RestreamDto` refuses to build a create payload without it.
- `list()` only returns rules created through the native API — rules defined in WMSPanel do not appear.
- Rules created through the native API are not persisted across a Nimble config reload or restart; recreate them as needed.

### Session Management

[](#session-management)

```
use AlexHackney\LaraNimble\Facades\Nimble;

// List all active sessions
$sessions = Nimble::sessions()->list();
foreach ($sessions as $session) {
    echo "Session {$session->id}: {$session->app}/{$session->stream}\n";
    echo "  Type: {$session->type}\n";           // HLS, MPEG-DASH, ...
    echo "  Client IP: {$session->clientIp}\n";
    echo "  Bytes sent: {$session->bytesSent}\n";
}

// Find one session (null when it does not exist)
$session = Nimble::sessions()->find(4);

// Terminate one or many sessions
Nimble::sessions()->terminate(4);
Nimble::sessions()->terminate([4, 5, 6]);
```

### DVR Management

[](#dvr-management)

```
use AlexHackney\LaraNimble\Facades\Nimble;

// Archive status for all DVR-enabled streams
$archives = Nimble::dvr()->status();
foreach ($archives as $archive) {
    echo "{$archive->stream}: {$archive->duration}s, {$archive->size} bytes\n";
}

// One stream, including its recorded period timeline
$archive = Nimble::dvr()->status('live', 'stream1', timeline: true)->first();
foreach ($archive->timeline as $period) {
    echo "Period {$period['period']}: {$period['start']} - {$period['end']}\n";
}

// Build a download URL for an MP4 export (auth params included automatically)
$url = Nimble::dvr()->exportMp4Url('live', 'stream1', start: 1700000000, end: 1700003600);

// Stream an MP4 export straight to a local file (no memory buffering,
// no request timeout by default — safe for long archives)
Nimble::dvr()->exportMp4ToFile('live', 'stream1', storage_path('exports/show.mp4'), start: 1700000000, end: 1700003600);

// Or download the MP4 bytes through PHP (small ranges only)
$mp4 = Nimble::dvr()->exportMp4('live', 'stream1', start: 1700000000, end: 1700003600);

// SRT subtitle export from a DVR range
$srtUrl = Nimble::dvr()->exportSrtUrl('live', 'stream1', start: 1700000000, end: 1700003600);
$srt = Nimble::dvr()->exportSrt('live', 'stream1', start: 1700000000, end: 1700003600, track: 1, lang: 'en');

// Reload an archive from disk
Nimble::dvr()->reload('live', 'stream1');

// Cleanup: keep only the most recent 60 minutes
Nimble::dvr()->cleanupArchive('live', 'stream1', targetDepth: 60);

// Cleanup: remove a specific range
Nimble::dvr()->cleanupArchive('live', 'stream1', from: 1700000000, to: 1700003600);
```

### Publish Control

[](#publish-control)

Requires publish control to be enabled in Nimble's RTMP settings.

```
use AlexHackney\LaraNimble\Facades\Nimble;

// List active publishers
$publishers = Nimble::publishControl()->status();
foreach ($publishers as $publisher) {
    echo "{$publisher->id}: {$publisher->stream} from {$publisher->ip}\n";
}

// Disconnect publishers by id
Nimble::publishControl()->deny('pub-1');
Nimble::publishControl()->deny(['pub-1', 'pub-2']);
```

### Server Management

[](#server-management)

```
use AlexHackney\LaraNimble\Facades\Nimble;

// Get server status
$status = Nimble::server()->status();
echo "Connections: {$status->connections}\n";
echo "Out rate: {$status->outRate}\n";
echo "RAM cache: {$status->ramCacheSize} / {$status->maxRamCacheSize}\n";
echo "File cache: {$status->fileCacheSize} / {$status->maxFileCacheSize}\n";
// $status->sysInfo holds the raw SysInfo array as returned by Nimble

// Reload server configuration (optionally including drm.conf)
Nimble::server()->reloadConfig();
Nimble::server()->reloadConfig(drm: true);

// Reload SSL certificates without a restart
Nimble::server()->reloadSslCertificates();

// Trigger settings sync with WMSPanel
Nimble::server()->syncPanelSettings();

// Server playlist status (raw array, shape defined by Nimble)
$playlists = Nimble::server()->playlistStatus();
```

### Data Cache

[](#data-cache)

Nimble's data cache holds responses for remote VOD and similar content, addressed by keys derived from origin URLs.

```
use AlexHackney\LaraNimble\Facades\Nimble;

// Resolve the cache key for an origin URL (null when unknown)
$key = Nimble::cache()->key('http://origin:8081/vod/sample.mp4');

// Evict cached items; returns the list of removed items
$removed = Nimble::cache()->delete($key);

// Dry run: report what would be removed without removing it
$wouldRemove = Nimble::cache()->delete($key, dryRun: true);
```

### Protocol Status &amp; Settings

[](#protocol-status--settings)

These endpoints return structures defined by Nimble or the respective protocol specs, so they are exposed as raw arrays.

```
use AlexHackney\LaraNimble\Facades\Nimble;

$rtmp = Nimble::protocols()->rtmpSettings();       // RtmpSettings structure
$mpegts = Nimble::protocols()->mpegtsStatus();
$cameras = Nimble::protocols()->mpegtsSettings();  // CamerasHash + Cameras
$srtOut = Nimble::protocols()->srtSenderStats();   // fields per SRT spec
$srtIn = Nimble::protocols()->srtReceiverStats();
$ristOut = Nimble::protocols()->ristSenderStats(); // fields per RIST spec
$ristIn = Nimble::protocols()->ristReceiverStats();
$ndi = Nimble::protocols()->ndiList();
```

### Icecast

[](#icecast)

```
use AlexHackney\LaraNimble\Facades\Nimble;

// Current metadata of an Icecast stream
$info = Nimble::icecast()->info('radio', 'main');
echo $info['icy-name'] ?? '';
echo $info['streamtitle'] ?? '';

// Inject new metadata
Nimble::icecast()->updateMetadata('radio', 'main', 'Artist - Song');
Nimble::icecast()->updateMetadata('radio', 'main', 'Artist - Song', 'https://example.com');
```

### SCTE-35 Ad Markers

[](#scte-35-ad-markers)

Requires the Nimble Advertizer feature.

```
use AlexHackney\LaraNimble\Facades\Nimble;

// Start an ad break (optionally with a duration in seconds)
Nimble::scte35()->cueOut('live', 'stream1', 30);

// End an ad break
Nimble::scte35()->cueIn('live', 'stream1');

// Insert a time_signal marker
Nimble::scte35()->timeSignal('live', 'stream1', segType: 52, upidType: 14, upid: 'abc123');
```

### Using in Controllers

[](#using-in-controllers)

```
