martincamen / laravel-sonarr
Laravel integration for Sonarr PHP SDK
Installs: 2
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/martincamen/laravel-sonarr
Requires
- php: ^8.3
- driftingly/rector-laravel: ^2.1
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- martincamen/sonarr-php: ^0.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- roave/security-advisories: dev-latest
This package is auto-updated.
Last update: 2026-01-21 07:22:39 UTC
README
Laravel integration for the Sonarr PHP SDK, providing a seamless experience for interacting with Sonarr using unified domain models from php-arr-core.
Important
This project is still being developed and breaking changes might occur even between patch versions.
The aim is to follow semantic versioning as soon as possible.
Ecosystem
| Package | Description |
|---|---|
| radarr-php | PHP SDK for Radarr |
| sonarr-php | PHP SDK for Sonarr |
| jellyseerr-php | PHP SDK for Jellyseerr |
| laravel-radarr | Laravel integration for Radarr |
| laravel-sonarr | Laravel integration for Sonarr |
| laravel-jellyseerr | Laravel integration for Jellyseerr |
Features
- Unified API using canonical domain models from
php-arr-core - Type-safe interactions with Sonarr
- Laravel facade with full IDE autocompletion
- Testing utilities for mocking responses
- Automatic service discovery via Laravel's package auto-discovery
Requirements
- PHP 8.3+
- Laravel 10.0+, 11.0+ or 12.0+
Installation
composer require martincamen/laravel-sonarr
The package will auto-register its service provider in Laravel.
Configuration
Publish the configuration file:
php artisan vendor:publish --provider="MartinCamen\LaravelSonarr\SonarrServiceProvider"
Add the following environment variables to your .env file:
SONARR_HOST=localhost SONARR_PORT=8989 SONARR_API_KEY=your-api-key SONARR_USE_HTTPS=false SONARR_TIMEOUT=30 SONARR_URL_BASE=
Configuration Options
| Option | Description | Default |
|---|---|---|
SONARR_HOST |
Hostname or IP address of your Sonarr server | localhost |
SONARR_PORT |
Port number for your Sonarr server | 8989 |
SONARR_API_KEY |
Your Sonarr API key (Settings > General > Security) | - |
SONARR_USE_HTTPS |
Use HTTPS for connections | false |
SONARR_TIMEOUT |
Request timeout in seconds | 30 |
SONARR_URL_BASE |
URL base for reverse proxy subpaths (e.g., /sonarr) |
- |
Usage
Using the Facade
The Sonarr facade provides access to the SDK client via an action-based API:
use MartinCamen\LaravelSonarr\Facades\Sonarr; // Get all active downloads $downloads = Sonarr::downloads()->all(); // Get all series $series = Sonarr::series()->all(); // Get a specific series by ID $show = Sonarr::series()->find(1); // Get system information $status = Sonarr::system()->status();
Dependency Injection
You can also inject Sonarr directly:
use MartinCamen\Sonarr\Sonarr; class SeriesController { public function __construct(private Sonarr $sonarr) {} public function index() { return view('series.index', ['series' => $this->sonarr->series()->all()]); } }
Working with Downloads
The downloads()->all() method returns a DownloadPage containing all active downloads:
use MartinCamen\LaravelSonarr\Facades\Sonarr; use MartinCamen\Sonarr\Data\Responses\DownloadPage; /** @var DownloadPage $downloadPage */ $downloadPage = Sonarr::downloads()->all(); // Check if there are any downloads echo "Active downloads: {$downloadPage->totalRecords}"; // Iterate over downloads foreach ($downloadPage as $download) { echo $download->title; echo $download->status->value; echo $download->sizeleft; } // Get a specific download $download = Sonarr::downloads()->find(1); // Get download status summary $status = Sonarr::downloads()->status(); echo "Total: {$status->totalCount}"; echo "Unknown: {$status->unknownCount}"; // Delete a download Sonarr::downloads()->delete(1); // Bulk delete downloads Sonarr::downloads()->bulkDelete([1, 2, 3]);
Working with Series
The series()->all() method returns a SeriesCollection:
use MartinCamen\LaravelSonarr\Facades\Sonarr; use MartinCamen\Sonarr\Data\Responses\Series; use MartinCamen\Sonarr\Data\Responses\SeriesCollection; // Get all series /** @var SeriesCollection $series */ $series = Sonarr::series()->all(); foreach ($series as $show) { echo "{$show->title} ({$show->year})"; echo $show->status->value; echo $show->monitored ? 'Monitored' : 'Not monitored'; } // Get a specific series by ID $show = Sonarr::series()->find(1); echo $show->title; // Search for series $results = Sonarr::series()->search('Breaking Bad'); // Search by TVDB ID $series = Sonarr::series()->searchByTvdb(81189); // Add a new series $series = Sonarr::series()->add([ 'title' => 'Breaking Bad', 'tvdbId' => 81189, 'qualityProfileId' => 1, 'rootFolderPath' => '/tv/', ]); // Update a series $series = Sonarr::series()->update(1, ['monitored' => false]); // Delete a series Sonarr::series()->delete(1);
System
use MartinCamen\LaravelSonarr\Facades\Sonarr; // Get system status $status = Sonarr::system()->status(); echo $status->version; echo $status->osName; // Get system health $health = Sonarr::system()->health(); foreach ($health->warnings() as $warning) { echo $warning->type . ': ' . $warning->message; } // Get disk space information $diskSpace = Sonarr::system()->diskSpace(); foreach ($diskSpace as $disk) { echo $disk->path . ': ' . $disk->freeSpace; } // Get system tasks $tasks = Sonarr::system()->tasks(); $task = Sonarr::system()->task(1); // Get available backups $backups = Sonarr::system()->backups();
Response Types
All responses use typed DTOs from the SDK:
| Type | Description |
|---|---|
SeriesCollection |
Collection of series |
Series |
Individual series with metadata |
EpisodeCollection |
Collection of episodes |
Episode |
Individual episode |
DownloadPage |
Paginated downloads |
Download |
Individual download item |
SystemStatus |
System status information |
HealthCheckCollection |
Collection of health checks |
DiskSpaceCollection |
Collection of disk space info |
Testing
Using the Fake
The package provides SonarrFake for testing:
use MartinCamen\LaravelSonarr\Facades\Sonarr; class SeriesTest extends TestCase { public function testDisplaysDownloads(): void { // Create a fake instance $fake = Sonarr::fake(); // Make request $response = $this->get('/downloads'); // Assert the method was called $fake->assertCalled('downloads'); $response->assertOk(); } public function testGetsAllSeries(): void { $fake = Sonarr::fake(); // Make request that calls series()->all() $this->get('/series'); // Assert called $fake->assertCalled('series'); } public function testNothingWasCalled(): void { $fake = Sonarr::fake(); // No API calls made $this->get('/about'); $fake->assertNothingCalled(); } }
Custom Responses
You can provide custom responses to the fake:
use MartinCamen\LaravelSonarr\Facades\Sonarr; use MartinCamen\Sonarr\Testing\Factories\DownloadFactory; use MartinCamen\Sonarr\Testing\Factories\SeriesFactory; use MartinCamen\Sonarr\Testing\Factories\SystemStatusFactory; public function testWithCustomSeries(): void { Sonarr::fake([ 'series' => SeriesFactory::makeMany(10), ]); $response = $this->get('/series'); $response->assertOk(); $response->assertViewHas('series'); } public function testWithCustomDownloads(): void { Sonarr::fake([ 'downloads' => [ 'page' => 1, 'pageSize' => 10, 'totalRecords' => 2, 'records' => DownloadFactory::makeMany(2), ], ]); $response = $this->get('/downloads'); $response->assertOk(); } public function testWithCustomSystemStatus(): void { Sonarr::fake([ 'systemSummary' => SystemStatusFactory::make([ 'version' => '4.0.0.0', 'isProduction' => true, ]), ]); $response = $this->get('/system'); $response->assertSee('4.0.0.0'); }
Assertion Methods
The fake provides several assertion methods:
use MartinCamen\LaravelSonarr\Facades\Sonarr; $fake = Sonarr::fake(); // Assert a method was called $fake->assertCalled('downloads'); // Assert a method was not called $fake->assertNotCalled('series'); // Assert a method was called with specific parameters $fake->assertCalledWith('seriesById', ['id' => 5]); // Assert a method was called a specific number of times $fake->assertCalledTimes('downloads', 3); // Assert nothing was called $fake->assertNothingCalled(); // Get all recorded calls $calls = $fake->getCalls();
Example: Building a Dashboard
use MartinCamen\LaravelSonarr\Facades\Sonarr; class DashboardController extends Controller { public function index() { // Get system status $status = Sonarr::system()->status(); $health = Sonarr::system()->health(); // Get active downloads $downloads = Sonarr::downloads()->all(); // Get all series $series = Sonarr::series()->all(); return view('dashboard', [ 'version' => $status->version, 'isHealthy' => $health->isEmpty(), 'downloads' => $downloads, 'downloadCount' => $downloads->totalRecords, 'seriesCount' => count($series), ]); } }
Error Handling
use MartinCamen\LaravelSonarr\Facades\Sonarr; use MartinCamen\ArrCore\Exceptions\AuthenticationException; use MartinCamen\ArrCore\Exceptions\ConnectionException; use MartinCamen\ArrCore\Exceptions\NotFoundException; try { $series = Sonarr::series()->find(999); } catch (AuthenticationException $e) { // Invalid API key return back()->with('error', 'Invalid Sonarr API key'); } catch (NotFoundException $e) { // Series not found abort(404, 'Series not found'); } catch (ConnectionException $e) { // Connection error logger()->error('Could not connect to Sonarr: ' . $e->getMessage()); return back()->with('error', 'Sonarr server unavailable'); }
License
The MIT License (MIT). Please see License File for more information.
Credits
Built on top of the Sonarr PHP SDK and php-arr-core.