laravel-jellyseerr maintained by martincamen
Laravel Jellyseerr
Laravel integration for the Jellyseerr PHP SDK, providing a seamless experience for interacting with Jellyseerr 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 Jellyseerr
- 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-jellyseerr
The package will auto-register its service provider in Laravel.
Configuration
Publish the configuration file:
php artisan vendor:publish --provider="MartinCamen\LaravelJellyseerr\JellyseerrServiceProvider"
Add the following environment variables to your .env file:
JELLYSEERR_HOST=localhost
JELLYSEERR_PORT=5055
JELLYSEERR_API_KEY=your-api-key
JELLYSEERR_USE_HTTPS=false
JELLYSEERR_TIMEOUT=30
JELLYSEERR_URL_BASE=
Configuration Options
| Option | Description | Default |
|---|---|---|
JELLYSEERR_HOST |
Hostname or IP address of your Jellyseerr server | localhost |
JELLYSEERR_PORT |
Port number for your Jellyseerr server | 5055 |
JELLYSEERR_API_KEY |
Your Jellyseerr API key (Settings > General > API Key) | - |
JELLYSEERR_USE_HTTPS |
Use HTTPS for connections | false |
JELLYSEERR_TIMEOUT |
Request timeout in seconds | 30 |
JELLYSEERR_URL_BASE |
URL base for reverse proxy subpaths (e.g., /jellyseerr) |
- |
Usage
Using the Facade
The Jellyseerr facade provides access to the SDK client via an action-based API:
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
// Get all media requests
$requests = Jellyseerr::requests()->all();
// Get a specific request by ID
$request = Jellyseerr::requests()->find(1);
// Search for media
$results = Jellyseerr::search()->search('Breaking Bad');
// Get system information
$status = Jellyseerr::system()->status();
Dependency Injection
You can also inject Jellyseerr directly:
use MartinCamen\Jellyseerr\Jellyseerr;
class RequestController
{
public function __construct(private Jellyseerr $jellyseerr) {}
public function index()
{
return view('requests.index', ['requests' => $this->jellyseerr->requests()->all()]);
}
}
Working with Requests
The requests() method provides access to media request management:
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
use MartinCamen\Jellyseerr\Data\Responses\RequestPage;
use MartinCamen\Jellyseerr\Data\Responses\MediaRequest;
// Get all requests (paginated)
/** @var RequestPage $requestPage */
$requestPage = Jellyseerr::requests()->all();
foreach ($requestPage as $request) {
echo $request->id;
echo $request->status->value;
}
// Get a specific request
$request = Jellyseerr::requests()->find(1);
// Get request counts
$count = Jellyseerr::requests()->count();
echo "Pending: {$count->pending}";
echo "Approved: {$count->approved}";
// Create a movie request
$request = Jellyseerr::requests()->movie(550); // TMDB ID
// Create a series request with specific seasons
$request = Jellyseerr::requests()->series(1396, [1, 2]); // TMDB ID, seasons
// Approve a request
$request = Jellyseerr::requests()->approve(1);
// Decline a request
$request = Jellyseerr::requests()->decline(1);
// Retry a failed request
$request = Jellyseerr::requests()->retry(1);
// Delete a request
Jellyseerr::requests()->delete(1);
Working with Movies
The movies() method provides access to movie details via TMDB:
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
use MartinCamen\Jellyseerr\Data\Responses\MovieDetails;
// Get movie details by TMDB ID
$movie = Jellyseerr::movies()->find(550);
echo $movie->title;
echo $movie->overview;
// Get movie recommendations
$recommendations = Jellyseerr::movies()->recommendations(550);
// Get similar movies
$similar = Jellyseerr::movies()->similar(550);
Working with Series
The series() method provides access to TV series details via TMDB:
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
use MartinCamen\Jellyseerr\Data\Responses\SeriesDetails;
// Get series details by TMDB ID
$series = Jellyseerr::series()->find(1396);
echo $series->name;
echo $series->overview;
// Get series recommendations
$recommendations = Jellyseerr::series()->recommendations(1396);
// Get similar series
$similar = Jellyseerr::series()->similar(1396);
// Get season details
$season = Jellyseerr::series()->season(1396, 1);
Searching Media
The search() method provides search and discovery functionality:
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
use MartinCamen\Jellyseerr\Data\Responses\SearchPage;
// Search for any media
$results = Jellyseerr::search()->search('Breaking Bad');
foreach ($results as $result) {
echo $result->title ?? $result->name;
echo $result->mediaType;
}
// Discover movies
$movies = Jellyseerr::search()->discoverMovies();
// Discover series
$series = Jellyseerr::search()->discoverSeries();
// Get trending media
$trending = Jellyseerr::search()->trending();
// Get upcoming movies
$upcoming = Jellyseerr::search()->upcoming();
Working with Users
The users() method provides access to user management:
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
use MartinCamen\Jellyseerr\Data\Responses\UserPage;
use MartinCamen\Jellyseerr\Data\Responses\User;
// Get all users
/** @var UserPage $users */
$users = Jellyseerr::users()->all();
foreach ($users as $user) {
echo $user->displayName;
echo $user->email;
}
// Get a specific user
$user = Jellyseerr::users()->find(1);
// Get the current authenticated user
$me = Jellyseerr::users()->me();
// Get a user's requests
$requests = Jellyseerr::users()->requests(1);
System
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
// Get system status
$status = Jellyseerr::system()->status();
echo $status->version;
// Get appdata information
$appdata = Jellyseerr::system()->appdata();
// Get public settings
$settings = Jellyseerr::system()->publicSettings();
Response Types
All responses use typed DTOs from the SDK:
| Type | Description |
|---|---|
RequestPage |
Paginated media requests |
MediaRequest |
Individual media request |
RequestCount |
Request count statistics |
MovieDetails |
Movie details from TMDB |
SeriesDetails |
TV series details from TMDB |
SearchPage |
Paginated search results |
UserPage |
Paginated users |
User |
Individual user |
SystemSummary |
System status information |
Testing
Using the Fake
The package provides JellyseerrFake for testing:
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
class RequestTest extends TestCase
{
public function testDisplaysRequests(): void
{
// Create a fake instance
$fake = Jellyseerr::fake();
// Make request
$response = $this->get('/requests');
// Assert the method was called
$fake->assertCalled('requests');
$response->assertOk();
}
public function testSearchesMedia(): void
{
$fake = Jellyseerr::fake();
// Make request that calls search()->search()
$this->get('/search?q=test');
// Assert called
$fake->assertCalled('search');
}
public function testNothingWasCalled(): void
{
$fake = Jellyseerr::fake();
// No API calls made
$this->get('/about');
$fake->assertNothingCalled();
}
}
Assertion Methods
The fake provides several assertion methods:
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
$fake = Jellyseerr::fake();
// Assert a method was called
$fake->assertCalled('requests');
// Assert a method was not called
$fake->assertNotCalled('movies');
// Assert a method was called a specific number of times
$fake->assertCalledTimes('requests', 3);
// Assert nothing was called
$fake->assertNothingCalled();
// Get all recorded calls
$calls = $fake->getCalls();
Example: Building a Request Dashboard
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
class DashboardController extends Controller
{
public function index()
{
// Get system status
$status = Jellyseerr::system()->status();
// Get request counts
$counts = Jellyseerr::requests()->count();
// Get recent requests
$requests = Jellyseerr::requests()->all();
// Get trending media for suggestions
$trending = Jellyseerr::search()->trending();
return view('dashboard', [
'version' => $status->version,
'pendingCount' => $counts->pending,
'approvedCount' => $counts->approved,
'requests' => $requests,
'trending' => $trending,
]);
}
}
Error Handling
use MartinCamen\LaravelJellyseerr\Facades\Jellyseerr;
use MartinCamen\ArrCore\Exceptions\AuthenticationException;
use MartinCamen\ArrCore\Exceptions\ConnectionException;
use MartinCamen\ArrCore\Exceptions\NotFoundException;
try {
$request = Jellyseerr::requests()->find(999);
} catch (AuthenticationException $e) {
// Invalid API key
return back()->with('error', 'Invalid Jellyseerr API key');
} catch (NotFoundException $e) {
// Request not found
abort(404, 'Request not found');
} catch (ConnectionException $e) {
// Connection error
logger()->error('Could not connect to Jellyseerr: ' . $e->getMessage());
return back()->with('error', 'Jellyseerr server unavailable');
}
License
The MIT License (MIT). Please see License File for more information.
Credits
Built on top of the Jellyseerr PHP SDK and php-arr-core.