Files
StashHandler/qbit_client.py
T
bvandeusen 6adfd77236 Add category filtering, default to copy mode, split dev/prod compose
- Default MOVE_MODE changed to copy so qBit can continue seeding
- Add QBIT_CATEGORY env var (default "Stash") to filter torrents
- Split compose into dev (build from source) and prod (pre-built image)
- Fix Dockerfile COPY glob needing trailing slash
- Update README and summary docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 11:38:17 -05:00

52 lines
1.8 KiB
Python

import logging
import requests
log = logging.getLogger(__name__)
class QBitClient:
def __init__(self, url, username, password):
self.url = url.rstrip("/")
self.username = username
self.password = password
self.session = requests.Session()
self._logged_in = False
def login(self):
"""Authenticate with qBittorrent and store the session cookie."""
resp = self.session.post(
f"{self.url}/api/v2/auth/login",
data={"username": self.username, "password": self.password},
)
resp.raise_for_status()
if resp.text.strip() == "Ok.":
self._logged_in = True
log.info("Logged in to qBittorrent at %s", self.url)
else:
raise RuntimeError(f"qBit login failed: {resp.text}")
def _request(self, method, endpoint, **kwargs):
"""Make an API request, re-authenticating on 403."""
if not self._logged_in:
self.login()
resp = self.session.request(method, f"{self.url}{endpoint}", **kwargs)
if resp.status_code == 403:
log.info("Session expired, re-authenticating")
self.login()
resp = self.session.request(method, f"{self.url}{endpoint}", **kwargs)
resp.raise_for_status()
return resp
def get_completed_torrents(self, category=None):
"""Return a list of completed torrent dicts from qBittorrent."""
params = {"filter": "completed"}
if category:
params["category"] = category
resp = self._request("GET", "/api/v2/torrents/info", params=params)
return resp.json()
def get_torrent_files(self, info_hash):
"""Return the file list for a specific torrent."""
resp = self._request("GET", "/api/v2/torrents/files", params={"hash": info_hash})
return resp.json()