martincamen / sonarr-php
PHP SDK for Sonarr REST API v3
Installs: 5
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/martincamen/sonarr-php
Requires
- php: ^8.3
- guzzlehttp/guzzle: ^7.0
- martincamen/php-arr-core: ^0.1
Requires (Dev)
- laravel/pint: ^1.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^12.0
- rector/rector: ^2.3
- roave/security-advisories: dev-latest
This package is auto-updated.
Last update: 2026-01-04 23:57:48 UTC
README
A PHP SDK for the Sonarr REST API v3.
Also available:
Requirements
- PHP 8.3+
Installation
composer require martincamen/sonarr-php
Quick Start
use MartinCamen\Sonarr\Sonarr; $sonarr = Sonarr::create( host: 'localhost', port: 8989, apiKey: 'your-api-key', useHttps: false, ); // Get all downloads (queue items) $downloads = $sonarr->downloads(); // Get all series $series = $sonarr->series(); // Get system status $status = $sonarr->system()->status(); // Get system summary $systemSummary = $sonarr->systemSummary();
Laravel Integration
For Laravel integration, use the laravel-sonarr package which provides facades, service provider, and configuration management.
Usage
Downloads (Queue)
Get active downloads using the unified downloads() method, which returns Core domain models compatible with other *arr services:
use MartinCamen\Sonarr\Sonarr; $sonarr = Sonarr::create('localhost', 8989, 'your-api-key'); // Get all active downloads $downloads = $sonarr->downloads(); foreach ($downloads as $item) { echo $item->name; echo $item->progress->percentage() . '%'; echo $item->status->value; echo $item->size->formatted(); } // Filter downloads by status $active = $downloads->active(); $completed = $downloads->completed(); $failed = $downloads->failed(); // Get total size and progress echo $downloads->totalSize()->formatted(); echo $downloads->totalProgress()->percentage() . '%';
Series
use MartinCamen\ArrCore\Domain\Media\Series; // Get all series /** @var Series[] $series */ $series = $sonarr->series(); foreach ($series as $show) { echo $show->title; echo $show->year; echo $show->status->value; echo $show->monitored ? 'Monitored' : 'Not monitored'; } // Get a specific series by ID /** @var Series $show */ $show = $sonarr->seriesById(1); echo $show->title; echo $show->overview;
System Status
use MartinCamen\Sonarr\Sonarr; /** @var Sonarr $sonarr */ echo $sonarr->system()->status()->version; foreach ($sonarr->system()->health()->warnings() as $warning) { echo $warning->type . ': ' . $warning->message; }
System Summary
use MartinCamen\ArrCore\Domain\System\SystemSummary; /** @var SystemSummary $summary */ $summary = $sonarr->systemSummary(); echo $summary->version; echo $summary->isHealthy ? 'Healthy' : 'Issues detected'; foreach ($summary->healthIssues as $issue) { echo $issue->type . ': ' . $issue->message; }
Episodes
Access episode information and management:
use MartinCamen\Sonarr\Data\Options\EpisodeOptions; use MartinCamen\Sonarr\Data\Responses\Episode; use MartinCamen\Sonarr\Data\Responses\EpisodeCollection; use MartinCamen\Sonarr\Sonarr; // Get all episodes for a series /** @var EpisodeCollection $episodes */ $episodes = $sonarr->episode()->forSeries(1); // Get episodes for a specific season $episodes = $sonarr->episode()->forSeries(1, seasonNumber: 2); // Get all episodes with custom filters $options = EpisodeOptions::make()->withIncludeImages(includeImages: true); $episodes = $sonarr->episode()->all($options); // Get a specific episode by ID /** @var Episode $episode */ $episode = $sonarr->episode()->get(1);
Episode Files
Access episode file information:
use MartinCamen\Sonarr\Actions\EpisodeFileActions; use MartinCamen\Sonarr\Data\Responses\EpisodeFile; use MartinCamen\Sonarr\Data\Responses\EpisodeFileCollection; /** @var EpisodeFileActions $episodeFiles */ $episodeFiles = $sonarr->episodeFile(); // Get all episode files for a series /** @var EpisodeFileCollection $files */ $files = $episodeFiles->all(seriesId: 1); // Get a specific episode file by ID /** @var EpisodeFile $file */ $file = $episodeFiles->find(1); // Delete an episode file $episodeFiles->delete(1);
Calendar
Access upcoming episode releases:
use MartinCamen\Sonarr\Actions\CalendarActions; use MartinCamen\Sonarr\Data\Options\CalendarOptions; use MartinCamen\Sonarr\Data\Responses\EpisodeCollection; // Get upcoming episodes (defaults to today to today + 2 days) /** @var CalendarActions $calendar */ $calendar = $sonarr->calendar(); /** @var EpisodeCollection $episodes */ $episodes = $calendar->all(); // Get episodes within a specific date range $options = CalendarOptions::make()->withDateRange( new DateTime('2024-01-01'), new DateTime('2024-01-31'), ); $episodes = $calendar->all($options); // Include unmonitored series $options = CalendarOptions::make() ->withUnmonitored(unmonitored: true) ->withTags([1, 2]); $episodes = $calendar->get($options);
History
Access download history:
use MartinCamen\ArrCore\Data\Options\PaginationOptions; use MartinCamen\ArrCore\Data\Options\SortOptions; use MartinCamen\Sonarr\Actions\HistoryActions; use MartinCamen\Sonarr\Data\Enums\HistoryEventType; use MartinCamen\Sonarr\Data\Options\HistoryOptions; use MartinCamen\Sonarr\Data\Responses\HistoryPage; // Get paginated history with defaults /** @var HistoryActions $history */ $history = $sonarr->history(); /** @var HistoryPage $historyPage */ $historyPage = $history->all(); // Get history with custom pagination and sorting $pagination = new PaginationOptions(page: 1, pageSize: 50); $sort = SortOptions::by('date')->descending(); $historyPage = $history->all($pagination, $sort); // Filter by event type $filters = HistoryOptions::make() ->withEventType(HistoryEventType::Grabbed) ->withIncludeSeries(true); $historyPage = $history->all(null, null, $filters);
Wanted (Missing & Cutoff)
Access missing episodes and quality cutoff:
use MartinCamen\ArrCore\Actions\WantedActions; use MartinCamen\ArrCore\Data\Options\WantedOptions; /** @var WantedActions $wanted */ $wanted = $sonarr->wanted(); // Get paginated missing episodes $missing = $wanted->missing(); // Filter to only monitored episodes $filters = WantedOptions::make()->onlyMonitored(); $missing = $wanted->missing(null, null, $filters); // Get ALL missing episodes (automatically handles pagination) $allMissing = $wanted->allMissing(); // Get episodes below quality cutoff $cutoff = $wanted->cutoff();
Commands
Execute Sonarr commands:
use MartinCamen\Sonarr\Actions\CommandActions; /** @var CommandActions $commands */ $commands = $sonarr->command(); // Get all commands $all = $commands->all(); // Trigger an RSS sync $command = $commands->rssSync(); // Execute a series search $command = $commands->searchSeries(id: 1); // Execute a season specific series search $command = $commands->searchSeason(seriesId: 1, seasonNumber: 4);
Advanced: Raw API Access
For operations not yet exposed through the SDK, use the api() method to access the low-level API client:
use MartinCamen\Sonarr\Data\Options\QueueOptions; use MartinCamen\Sonarr\Sonarr; // Add a new series $seriesData = [ 'title' => 'Breaking Bad', 'qualityProfileId' => 1, 'tvdbId' => 81189, 'rootFolderPath' => '/tv/', 'monitored' => true, 'addOptions' => [ 'searchForMissingEpisodes' => true, ], ]; /** @var Sonarr $sonarr */ $sonarr->api()->series()->add($seriesData); // Update a series $sonarr->api()->series()->update(1, $seriesData); // Delete a series $sonarr->api()->series()->delete(1, deleteFiles: true); // Search for series $results = $sonarr->api()->series()->lookup('Breaking Bad'); // Get queue with full options $pagination = new PaginationOptions(page: 1, pageSize: 100); $sort = SortOptions::by('timeleft')->ascending(); $filters = QueueOptions::make()->withIncludeSeries(true); $queue = $sonarr->api()->queue()->all($pagination, $sort, $filters); // Delete from queue $sonarr->api()->queue()->delete( id: 1, removeFromClient: true, blocklist: false, ); // Get disk space $diskSpace = $sonarr->api()->system()->diskSpace();
Request Options
The SDK provides typed request option classes:
Pagination Options
use MartinCamen\ArrCore\Data\Options\PaginationOptions; $options = PaginationOptions::make(pageSize: 12); $options = new PaginationOptions(page: 2, pageSize: 50); $options = PaginationOptions::make()->withPage(3)->withPageSize(100);
Sort Options
use MartinCamen\ArrCore\Data\Options\SortOptions; $options = SortOptions::none(); $options = SortOptions::by('title')->ascending(); $options = SortOptions::by('airDateUtc')->descending();
Error Handling
The SDK throws specific exceptions for different error types:
use MartinCamen\ArrCore\Exceptions\AuthenticationException; use MartinCamen\ArrCore\Exceptions\ConnectionException; use MartinCamen\ArrCore\Exceptions\NotFoundException; use MartinCamen\ArrCore\Exceptions\ValidationException; try { $show = $sonarr->seriesById(999); } catch (AuthenticationException $e) { // Invalid API key } catch (NotFoundException $e) { // Series not found } catch (ConnectionException $e) { // Could not connect to server } catch (ValidationException $e) { // Validation error print_r($e->getErrors()); }
Testing
The SDK provides testing utilities for easy mocking:
use PHPUnit\Framework\Attributes\Test; use MartinCamen\Sonarr\Testing\Factories\DownloadFactory; use MartinCamen\Sonarr\Testing\Factories\SeriesFactory; use MartinCamen\Sonarr\Testing\SonarrFake; class MyTest extends TestCase { #[Test] public function itDisplaysSeries(): void { $fake = new SonarrFake([ 'series' => SeriesFactory::makeMany(5), ]); $series = $fake->series(); $this->assertCount(5, $series); $fake->assertCalled('series'); $fake->assertCalledTimes('series', 1); } #[Test] public function itDisplaysDownloads(): void { $fake = new SonarrFake([ 'downloads' => DownloadFactory::makeMany(3), ]); $downloads = $fake->downloads(); $this->assertCount(3, $downloads); $fake->assertCalled('downloads'); } }
Using Factories
use MartinCamen\Sonarr\Testing\Factories\DownloadFactory; use MartinCamen\Sonarr\Testing\Factories\SeriesFactory; // Create series data $series = SeriesFactory::make(1); $series = SeriesFactory::makeMany(5); $series = SeriesFactory::make(1, [ 'title' => 'Breaking Bad', 'tvdbId' => 81189, 'year' => 2008, ]); // Create download data $downloads = DownloadFactory::makeMany(3); $completed = DownloadFactory::makeCompleted(1); $withError = DownloadFactory::makeWithError(2);
Architecture
The SDK follows a layered architecture:
Sonarr (Public SDK)
↓
SonarrApiClient (Internal API Client)
↓
HTTP Client
Sonarr: The public interface with unified terminology (downloads(),series()) returning Core domain modelsSonarrApiClient: Internal client using Sonarr's native API terminology (queue(),series())- Core Domain Models: Shared types from
php-arr-corefor cross-service compatibility
License
The MIT License (MIT). Please see License File for more information.