Files
minstrel/internal/db/migrations/0079_acquisition_services.up.sql
T
bvandeusenandClaude Opus 5.5 a2e1890d4c
release / govulncheck (push) Successful in 34s
release / web (push) Successful in 1m13s
release / go (push) Successful in 2m12s
release / integration (push) Successful in 4m6s
release / Build signed APK (releases and dev) (push) Successful in 8m41s
release / android (push) Successful in 9m47s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 59s
release / Verify release artifacts (tag releases only) (push) Skipped
feat: indexers and download clients you add and configure (M502 #5518)
Admin → Integrations gains "Indexers and download clients": two lists of
connections the operator adds, Fabled Librarian's model. Nothing is seeded
or copied from Lidarr.

- Indexers: Prowlarr (its native /api/v1/search, which asks every indexer
  at once; Prowlarr has no aggregate Torznab feed) and any Torznab feed.
- Download clients: qBittorrent (WebUI v2, with re-login on an expired
  session) and FabledExchange, one entry with a switch per job:
  downloads (/api/downloads), search (/api/search) and release details
  (/api/torrents/{infohash}). All three start on; its key is optional.
- Each entry: enable, name, address, a write-only key or password, Test.
  A download client's Test lists its torrents and checks the downloads
  folder is visible and writable from Minstrel; an Exchange's Test
  reports each job switched on.
- Download clients carry a path mapping (Minstrel's /downloads vs the
  client's own name for it); search sources carry searches per hour.

Migration 0079 adds acquisition_services with its CHECKs. Ported from
Fabled Librarian: internal/indexer (Torznab, 429 Retry-After, magnet
redirects), internal/prowlarr, internal/exchange, internal/downloader,
and internal/acquire's one builder per kind, which Test already uses
so a passing Test vouches for the path a grab will take.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:34:51 -04:00

64 lines
3.5 KiB
SQL

-- 0079_acquisition_services.up.sql — the indexers and download clients the
-- operator adds in Admin → Integrations (Scribe milestone #502, #5518).
--
-- Minstrel replaces Lidarr, so it searches and downloads itself. Nothing is
-- seeded and nothing is copied from Lidarr: a connection is a row the operator
-- adds, from a list of what Minstrel can speak. Ported from Fabled
-- Librarian's acquisition_services (its migrations 0044, 0055, 0062, 0064,
-- 0070), which settled the shape:
--
-- kind which list the row appears in: search (Indexers) or download
-- (Download clients).
-- protocol which client is built: prowlarr (its native /api/v1/search,
-- because Prowlarr has no aggregate Torznab feed), torznab (any
-- single feed), qbittorrent (the WebUI v2), exchange.
--
-- FabledExchange is ONE row under Download clients with a switch per job:
-- downloading, searching its DHT index, and describing a torrent's files and
-- swarm before a grab. The serves_* switches mean something on an exchange
-- row only; every other row does the one job its kind names.
--
-- Secrets (api_key, password) are write-only: no query the API answers with
-- returns them, only whether one is set.
CREATE TABLE acquisition_services (
-- code is the stable identity: the protocol for the first of its kind,
-- then protocol-2, protocol-3. Legible in a log, never typed.
code text PRIMARY KEY,
kind text NOT NULL
CONSTRAINT acquisition_services_kind_check CHECK (kind IN ('search', 'download')),
protocol text NOT NULL
CONSTRAINT acquisition_services_protocol_check
CHECK (protocol IN ('prowlarr', 'torznab', 'qbittorrent', 'exchange')),
label text NOT NULL,
-- A new row is off: it has no address yet.
enabled boolean NOT NULL DEFAULT false,
-- The address as the operator's browser opens it. For a Torznab feed it
-- is the whole feed URL up to and including /api.
base_url text NOT NULL DEFAULT '',
api_key text NOT NULL DEFAULT '',
-- qBittorrent only. Both may be empty: a WebUI with auth bypassed for
-- local addresses takes neither.
username text NOT NULL DEFAULT '',
password text NOT NULL DEFAULT '',
-- Download clients: everything Minstrel adds is filed under it.
category text NOT NULL DEFAULT '',
timeout_ms integer NOT NULL DEFAULT 30000
CONSTRAINT acquisition_services_timeout_check CHECK (timeout_ms BETWEEN 1000 AND 300000),
serves_search boolean NOT NULL DEFAULT true,
serves_downloads boolean NOT NULL DEFAULT true,
serves_enrichment boolean NOT NULL DEFAULT true,
-- One downloads folder, two names: where Minstrel has it mounted, and
-- where the client sees it (empty = the same path, nothing translated).
local_path text NOT NULL DEFAULT '/downloads'
CONSTRAINT acquisition_services_local_path_check CHECK (local_path LIKE '/%'),
remote_path text NOT NULL DEFAULT ''
CONSTRAINT acquisition_services_remote_path_check CHECK (remote_path = '' OR remote_path LIKE '/%'),
-- The most searches the schedule sends this source an hour; 0 = unpaced.
-- Prowlarr's indexers answer 429 when asked too often; an Exchange
-- searches its own database and starts unpaced.
searches_per_hour integer NOT NULL DEFAULT 120
CONSTRAINT acquisition_services_searches_per_hour_check CHECK (searches_per_hour BETWEEN 0 AND 3600),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);