npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xmer/qbittorrent-client

v1.0.0

Published

TypeScript client for qBittorrent Web API with torrent management and progress monitoring

Readme

@xmer/qbittorrent-client

npm version License: MIT TypeScript Test Coverage

TypeScript client for qBittorrent Web API with torrent management and progress monitoring.

Features

  • Full qBittorrent Web API v2 support
  • TypeScript with strict mode - Complete type safety
  • Event-driven progress monitoring - Real-time download progress
  • Auto-reauthentication - Handles session expiry automatically
  • Category management - Organize torrents with categories
  • Comprehensive error handling - Specific error types for all scenarios
  • 95% test coverage - Production-ready reliability
  • Input validation - Magnet link validation before processing

Installation

npm install @xmer/qbittorrent-client

Prerequisites

  • Node.js >= 16.0.0
  • qBittorrent with Web UI enabled

Enabling qBittorrent Web UI

  1. Open qBittorrent
  2. Go to ToolsOptionsWeb UI
  3. Check Enable the Web User Interface (Remote control)
  4. Set your username and password (default: admin / adminadmin)
  5. Note the port (default: 8080)

Quick Start

import { QBittorrentClient } from '@xmer/qbittorrent-client';

// Create client
const client = new QBittorrentClient({
  apiUrl: 'http://localhost:8080',
  username: 'admin',
  password: 'adminadmin'
});

// Connect
await client.connect();

// Add torrent
const hash = await client.addTorrent('magnet:?xt=urn:btih:...');

// Monitor progress
client.startProgressMonitoring(hash);

client.on('progress', (progress) => {
  console.log(`${progress.name}: ${progress.progress.toFixed(1)}%`);
  console.log(`Speed: ${(progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s`);
  console.log(`ETA: ${progress.timeRemaining}`);
});

client.on('completed', (torrent) => {
  console.log(`Download completed: ${torrent.name}`);
  client.stopProgressMonitoring(hash);
});

// Cleanup
await client.disconnect();

API Reference

QBittorrentClient

Constructor

new QBittorrentClient(config: QBittorrentConfig)

Config Options:

interface QBittorrentConfig {
  apiUrl: string;                  // Required: qBittorrent Web API URL
  username: string;                // Required: Web UI username
  password: string;                // Required: Web UI password
  defaultCategory?: string;        // Optional: Default category for torrents
  pollInterval?: number;           // Optional: Progress poll interval (default: 5000ms)
  timeout?: number;                // Optional: Request timeout (default: 10000ms)
  retryAttempts?: number;          // Optional: Retry attempts (default: 3)
  verifySsl?: boolean;             // Optional: Verify SSL certs (default: true)
}

Lifecycle Methods

await client.connect(): Promise<void>
await client.disconnect(): Promise<void>
await client.healthCheck(): Promise<boolean>

Torrent Operations

// Add torrent
await client.addTorrent(
  magnetLink: string,
  options?: AddTorrentOptions
): Promise<string> // Returns torrent hash

// Get torrent info
await client.getTorrent(hash: string): Promise<Torrent>

// List torrents
await client.listTorrents(filter?: TorrentFilter): Promise<Torrent[]>

// Control torrents
await client.pauseTorrent(hash: string): Promise<void>
await client.resumeTorrent(hash: string): Promise<void>
await client.removeTorrent(hash: string, deleteFiles?: boolean): Promise<void>

Category Management

// Create category
await client.createCategory(name: string, savePath?: string): Promise<void>

// List categories
await client.getCategories(): Promise<Category[]>

// Delete category
await client.deleteCategory(name: string): Promise<void>

// Assign category to torrent
await client.setTorrentCategory(hash: string, category: string): Promise<void>

Progress Monitoring

// Get current progress
await client.getTorrentProgress(hash: string): Promise<TorrentProgress>

// Start monitoring
client.startProgressMonitoring(hash: string, interval?: number): void

// Stop monitoring
client.stopProgressMonitoring(hash: string): void

Events

// Progress updates
client.on('progress', (progress: TorrentProgress) => void)

// Completion events
client.on('completed', (torrent: Torrent) => void)

// Error events
client.on('error', (error: Error, hash: string) => void)

Static Utilities

// Extract hash from magnet link
QBittorrentClient.extractTorrentHash(magnetLink: string): string | null

// Validate magnet link
QBittorrentClient.validateMagnetLink(magnetLink: string): boolean

Usage Examples

Basic Download

const client = new QBittorrentClient({
  apiUrl: 'http://localhost:8080',
  username: 'admin',
  password: 'adminadmin'
});

await client.connect();

const hash = await client.addTorrent(
  'magnet:?xt=urn:btih:c9e15763f722f23e98a29decdfae341b98d53056'
);

console.log(`Torrent added with hash: ${hash}`);

await client.disconnect();

Progress Monitoring

const hash = await client.addTorrent('magnet:?xt=urn:btih:...');

client.startProgressMonitoring(hash);

client.on('progress', (progress) => {
  console.log(`Progress: ${progress.progress.toFixed(2)}%`);
  console.log(`Download: ${(progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s`);
  console.log(`Upload: ${(progress.uploadSpeed / 1024 / 1024).toFixed(2)} MB/s`);
  console.log(`ETA: ${progress.timeRemaining}`);
  console.log(`Downloaded: ${(progress.bytesDownloaded / 1024 / 1024 / 1024).toFixed(2)} GB`);
  console.log(`Total: ${(progress.bytesTotal / 1024 / 1024 / 1024).toFixed(2)} GB`);
});

client.on('completed', (torrent) => {
  console.log(`✓ Download completed: ${torrent.name}`);
  console.log(`Ratio: ${torrent.ratio.toFixed(2)}`);
  client.stopProgressMonitoring(hash);
});

client.on('error', (error, hash) => {
  console.error(`Error monitoring ${hash}:`, error.message);
});

Category Management

// Create categories
await client.createCategory('Linux', '/downloads/linux');
await client.createCategory('Movies', '/downloads/movies');
await client.createCategory('Games', '/downloads/games');

// Add torrent to category
const hash = await client.addTorrent('magnet:?xt=urn:btih:...', {
  category: 'Linux'
});

// Change category
await client.setTorrentCategory(hash, 'Movies');

// List all categories
const categories = await client.getCategories();
categories.forEach(cat => {
  console.log(`${cat.name}: ${cat.savePath}`);
});

// Delete category
await client.deleteCategory('Games');

Advanced Options

const hash = await client.addTorrent('magnet:?xt=urn:btih:...', {
  category: 'Downloads',
  savePath: '/custom/path',
  paused: false,                    // Start immediately
  skipHashCheck: false,
  sequentialDownload: true,          // Download in order
  firstLastPiecePrio: true,          // Prioritize first/last pieces
  dlLimit: 5242880,                  // 5 MB/s download limit
  upLimit: 1048576                   // 1 MB/s upload limit
});

Filtering Torrents

// Get all torrents
const allTorrents = await client.listTorrents();

// Filter by category
const linuxTorrents = await client.listTorrents({
  category: 'Linux'
});

// Filter by state
import { TorrentState } from '@xmer/qbittorrent-client';

const downloading = await client.listTorrents({
  state: TorrentState.Downloading
});

const seeding = await client.listTorrents({
  state: TorrentState.Uploading
});

// Filter by specific hashes
const specificTorrents = await client.listTorrents({
  hashes: [hash1, hash2, hash3]
});

Error Handling

import {
  QBittorrentAuthError,
  QBittorrentConnectionError,
  InvalidMagnetLinkError,
  TorrentNotFoundError,
  CategoryExistsError
} from '@xmer/qbittorrent-client';

try {
  await client.connect();
} catch (error) {
  if (error instanceof QBittorrentAuthError) {
    console.error('Authentication failed - check credentials');
  } else if (error instanceof QBittorrentConnectionError) {
    console.error(`Cannot connect to ${error.apiUrl}`);
  }
}

try {
  await client.addTorrent('invalid-magnet-link');
} catch (error) {
  if (error instanceof InvalidMagnetLinkError) {
    console.error('Invalid magnet link format');
  }
}

try {
  await client.getTorrent('nonexistent-hash');
} catch (error) {
  if (error instanceof TorrentNotFoundError) {
    console.error('Torrent not found');
  }
}

try {
  await client.createCategory('ExistingCategory');
} catch (error) {
  if (error instanceof CategoryExistsError) {
    console.error('Category already exists');
  }
}

Type Definitions

Torrent

interface Torrent {
  hash: string;                    // 40-character hex hash
  name: string;
  size: number;                    // Total size in bytes
  progress: number;                // Progress 0-1
  dlspeed: number;                 // Download speed bytes/sec
  upspeed: number;                 // Upload speed bytes/sec
  eta: number;                     // ETA in seconds (-1 = unknown)
  state: TorrentState;
  category: string;
  savePath: string;
  addedOn: number;                 // Unix timestamp
  completionOn: number;            // Unix timestamp (-1 if not completed)
  ratio: number;                   // Seeding ratio
  numSeeds: number;
  numLeechers: number;
}

TorrentProgress

interface TorrentProgress {
  hash: string;
  name: string;
  progress: number;                // 0-100
  downloadSpeed: number;           // bytes/sec
  uploadSpeed: number;             // bytes/sec
  eta: number;                     // seconds
  state: TorrentState;
  bytesDownloaded: number;
  bytesTotal: number;
  timeRemaining: string;           // Human-readable (e.g., "5m 32s")
}

TorrentState

enum TorrentState {
  Downloading = 'downloading',
  Uploading = 'uploading',        // Seeding
  PausedDL = 'pausedDL',
  PausedUP = 'pausedUP',
  Error = 'error',
  // ... and more
}

Development

Running Tests

# All tests
npm test

# Unit tests only
npm test -- tests/unit

# Integration tests only
npm test -- tests/integration

# With coverage
npm test -- --coverage

# Watch mode
npm test -- --watch

Building

npm run build

Linting

npm run lint
npm run lint:fix

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT © xmer

Changelog

See CHANGELOG.md for version history.

Support