Skip to content

Latest commit

 

History

History
390 lines (294 loc) · 9.12 KB

File metadata and controls

390 lines (294 loc) · 9.12 KB

Usage Guide

This guide provides comprehensive documentation for using the TsArr TypeScript SDK to interact with Radarr, Sonarr, Lidarr, Readarr, Prowlarr, Bazarr, qBittorrent, Seerr and Jellyfin.

Installation

bun add tsarr

Basic Setup

All clients follow the same initialization pattern:

import { RadarrClient, SonarrClient, LidarrClient, ReadarrClient, ProwlarrClient, QBittorrentClient, SeerrClient, JellyfinClient } from 'tsarr';

// Initialize a client
const radarr = new RadarrClient({
  baseUrl: 'http://localhost:7878',
  apiKey: 'your-radarr-api-key'
});

const sonarr = new SonarrClient({
  baseUrl: 'http://localhost:8989', 
  apiKey: 'your-sonarr-api-key'
});

// qBittorrent uses username/password instead of API keys
const qbit = new QBittorrentClient({
  baseUrl: 'http://localhost:8080',
  username: 'admin',
  password: 'adminadmin'
});

// Seerr (also works with Jellyseerr and Overseerr)
const seerr = new SeerrClient({
  baseUrl: 'http://localhost:5055',
  apiKey: 'your-seerr-api-key'
});

// Jellyfin — API key from Dashboard -> Advanced -> API Keys
const jellyfin = new JellyfinClient({
  baseUrl: 'http://localhost:8096',
  apiKey: 'your-jellyfin-api-key'
});

Note: Jellyfin serves PascalCase JSON (Id, Name, Items), unlike the camelCase used by the Servarr services. The generated types reflect this.

Environment Variables

For security, use environment variables for API keys:

const radarr = new RadarrClient({
  baseUrl: process.env.RADARR_BASE_URL || 'http://localhost:7878',
  apiKey: process.env.RADARR_API_KEY!
});

Common Operations

System Information

// Get system status
const status = await radarr.getSystemStatus();
console.log(`Version: ${status.data?.version}`);

// Health check
const health = await radarr.getHealth();
console.log(`Health issues: ${health.data?.length || 0}`);

Managing Movies (Radarr)

// Get all movies
const movies = await radarr.getMovies();

// Get specific movie
const movie = await radarr.getMovieById(123);

// Search for movies
const searchResults = await radarr.searchMovies('Inception');

// Add a movie
const newMovie = await radarr.addMovie({
  title: 'The Matrix',
  year: 1999,
  tmdbId: 603,
  qualityProfileId: 1,
  monitored: true,
  rootFolderPath: '/media/movies'
});

// Update movie
const updatedMovie = await radarr.updateMovie(movieId, {
  monitored: false
});

// Delete movie
await radarr.deleteMovie(movieId, { deleteFiles: true });

Managing TV Shows (Sonarr)

// Get all series
const series = await sonarr.getSeries();

// Get specific series
const show = await sonarr.getSeriesById(123);

// Search for series
const searchResults = await sonarr.searchSeries('Breaking Bad');

// Add series
const newSeries = await sonarr.addSeries({
  title: 'Breaking Bad',
  tvdbId: 81189,
  qualityProfileId: 1,
  monitored: true,
  rootFolderPath: '/media/tv'
});

Queue Management

// Get download queue
const queue = await radarr.getQueue();

// Remove item from queue
await radarr.deleteQueueItem(queueId);

// Search releases for a movie
const releases = await radarr.getRelease(movieId);

// Grab/push a release
await radarr.addRelease(release);

Lidarr metadata profiles and release selection

// Inspect profiles before adding an artist
const metadataProfiles = await lidarr.getMetadataProfiles();
const metadataProfile = await lidarr.getMetadataProfile(4);

// Search complete release resources for one album
const albumCandidates = await lidarr.getRelease(albumId);

// Or search all monitored albums for one artist
const artistCandidates = await lidarr.getRelease(undefined, artistId);

// Post one candidate back unchanged to grab it
const candidate = albumCandidates.data?.[0];
if (candidate) {
  await lidarr.addRelease(candidate);
}

Quality Management

// Get quality profiles
const profiles = await radarr.getQualityProfiles();

// Get quality definitions
const definitions = await radarr.getQualityDefinitions();

// Update quality definition
await radarr.updateQualityDefinition(defId, {
  minSize: 1000,
  maxSize: 5000
});

File Management

Each service exposes file management methods for its media type:

// Radarr - Movie files
const movieFiles = await radarr.getMovieFiles([movieId]);
const movieFile = await radarr.getMovieFile(fileId);
await radarr.deleteMovieFile(fileId);

// Sonarr - Episode files
const episodeFiles = await sonarr.getEpisodeFiles(seriesId);
const episodeFile = await sonarr.getEpisodeFile(fileId);
await sonarr.deleteEpisodeFile(fileId);

// Lidarr - Track files
const trackFiles = await lidarr.getTrackFiles(artistId);
const trackFile = await lidarr.getTrackFile(fileId);
await lidarr.deleteTrackFile(fileId);

// Readarr - Book files
const bookFiles = await readarr.getBookFiles(authorId);
const bookFile = await readarr.getBookFile(fileId);
await readarr.deleteBookFile(fileId);

Error Handling

TsArr provides specific error types for different scenarios:

import { ApiKeyError, ConnectionError, NotFoundError, ValidationError } from 'tsarr';

try {
  const movies = await radarr.getMovies();
} catch (error) {
  if (error instanceof ApiKeyError) {
    console.error('Invalid API key');
  } else if (error instanceof ConnectionError) {
    console.error('Could not connect to Radarr');
  } else if (error instanceof NotFoundError) {
    console.error('Resource not found');
  } else if (error instanceof ValidationError) {
    console.error('Invalid request data');
  }
}

Advanced Usage

Bulk Operations

// Import multiple movies
const importResults = await radarr.importMovies([
  { path: '/path/to/movie1.mkv', movieId: 1 },
  { path: '/path/to/movie2.mkv', movieId: 2 }
]);

// Delete multiple movies
await radarr.deleteMovies([1, 2, 3], { deleteFiles: true });

// Update multiple series
await sonarr.updateSeries([
  { id: 1, monitored: false },
  { id: 2, monitored: true }
]);

Custom Filters and Search

// Search with filters
const recentMovies = await radarr.getMovies({
  sortKey: 'dateAdded',
  sortDirection: 'descending',
  page: 1,
  pageSize: 50
});

// Filter by status
const monitoredMovies = await radarr.getMovies({
  monitored: true
});

Configuration Management

// Get download client settings
const downloadClients = await radarr.getDownloadClients();

// Get indexer settings  
const indexers = await radarr.getIndexers();

// Update naming configuration
await radarr.updateNamingConfig({
  renameMovies: true,
  movieFolderFormat: '{Movie Title} ({Release Year})'
});

TypeScript Tips

Type Safety

All API responses are fully typed. Use TypeScript's intellisense for available properties:

const movie = await radarr.getMovieById(123);
// movie.data is typed with all available properties
console.log(movie.data?.title); // ✅ Type-safe
console.log(movie.data?.invalidProp); // ❌ TypeScript error

Async/Await vs Promises

All client methods return promises and work with both async/await and .then():

// Async/await (recommended)
const movies = await radarr.getMovies();

// Promise chains
radarr.getMovies()
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

Performance Considerations

Connection Pooling

TsArr uses Bun's native fetch API which handles connection pooling automatically.

Rate Limiting

Be mindful of API rate limits. For bulk operations, add delays:

for (const movie of movies) {
  await processMovie(movie);
  await new Promise(resolve => setTimeout(resolve, 100)); // 100ms delay
}

Memory Usage

For large libraries, use pagination:

let page = 1;
let allMovies = [];

while (true) {
  const response = await radarr.getMovies({ page, pageSize: 100 });
  if (!response.data?.length) break;
  
  allMovies.push(...response.data);
  page++;
}

Configuration Examples

Multi-Instance Setup

// Production instances
const prodRadarr = new RadarrClient({
  baseUrl: 'https://radarr.example.com',
  apiKey: process.env.PROD_RADARR_API_KEY!
});

const prodSonarr = new SonarrClient({
  baseUrl: 'https://sonarr.example.com', 
  apiKey: process.env.PROD_SONARR_API_KEY!
});

// Development instances
const devRadarr = new RadarrClient({
  baseUrl: 'http://localhost:7878',
  apiKey: process.env.DEV_RADARR_API_KEY!
});

Every client owns its configuration, so instances of the same service never interfere with each other — prodRadarr keeps its base URL and API key when devRadarr is constructed. The same holds for updateConfig() on one instance.

Custom Headers and Options

const radarr = new RadarrClient({
  baseUrl: 'http://localhost:7878',
  apiKey: 'your-api-key',
  // Custom fetch options are passed through
  timeout: 30000,
  headers: {
    'User-Agent': 'MyApp/1.0'
  }
});

Next Steps