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

0.1.2 2026-01-21 07:22 UTC

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.