Auto-Tagger: criteria-based tagging for Sonarr/Radarr — companion to Label Sync. A new automation feature that applies tags to *arr items when they match a rule's criteria DSL. Same expressiveness as Library Cleanup (50+ rule types: genre, year, codec, watch state, Plex labels/collections, custom format score, audio channels, runtime, file path regex, etc.) plus full composite (AND/OR) rules and real-time tagging via Sonarr/Radarr Connect webhooks. Pair with Label Sync to mirror the tag onto Plex/Jellyfin/Emby labels — Auto-Tagger seeds the source tag, Label Sync propagates it. New /auto-tag page in the Maintenance section.
New documentation
The wiki gains two brand-new pages for this release:
- Auto-Tagger Guide — overview, two trigger paths (5min scheduler tick + sub-second Connect webhook), per-rule lock semantics, composite AND/OR rules, full Connect webhook setup walk-through, list-membership rule types (TMDb + Trakt), operator env (
TRAKT_CLIENT_ID), troubleshooting, API reference. - Label Sync Guide — overview of the any-to-any 5-service propagation model, scheduler cadence, additive merge semantics, composition with Auto-Tagger, troubleshooting, API reference.
Added
- Auto-Tagger feature at
/auto-tag— schedule-driven (5min ticks, 60min per-rule cooldown) and on-demand "Run now" against Sonarr/Radarr. Reuses Library Cleanup's evaluator + 50+ rule types, so rule expressiveness is at parity with cleanup from day one (#394). - Composite (AND/OR) rule builder UI — toggle between single criterion and composite mode in the rule dialog, add/remove condition rows independently, each row picks its own rule type with type-specific params via the shared
ConditionParamsFields(#395). - Sonarr/Radarr Connect webhook at
POST /api/auto-tag/webhook/:instanceId— fires within seconds of an import event for sub-second tagging vs. the 5-minute scheduled tick. Per-user Bearer-token auth (token's SHA-256 hash stored at rest; plaintext shown once at generation/rotation, never persisted). Webhook config panel on the Auto-Tagger page with copy-secret, rotate-secret, and incognito-mode redaction (#396). - Per-rule execution lock — prevents the scheduler tick and on-demand "Run now" from racing on the same rule's
series.update/movie.updateand dropping each other's tag merges. Skipped runs return HTTP 409 with a clear message (#398). - List-membership rule type —
tmdb_list_memberandtrakt_list_memberrule types in the criteria DSL, with backingTmdbListCache/TraktListCacheschema. Rules can be created and edited via the new "Lists" optgroup in the rule dialog (#399). - List-membership runtime: TMDb v3 + Trakt PAT integration — list-membership rules now match items at execution time. TMDb lookups reuse the per-user TMDb v3 read-access token already configured in Settings → Account (the same key Discover uses). Trakt lookups use a new per-user Trakt personal access token field in Settings → Account, paired with the operator-supplied
TRAKT_CLIENT_IDenv var. Both list caches refresh every 4 hours and orphan-collect rows for unreferenced lists at the end of each tick (#403).
Changed
- Generic rule-criteria types extracted from
library-cleanupinto a neutral shared module —RuleType,Condition,CompositeOperator, all*RuleParamsschemas, the validation map, and the data-source-dependency map now live inpackages/shared/src/types/rule-criteria.ts. Both Auto-Tagger and Library Cleanup consume from there (#393). ConditionParamsFieldsandMultiSelectFieldhoisted out oflibrary-cleanup/components/into a sharedfeatures/rule-criteria/components/module (#397).
Fixed
- TRaSH Guides cache refresh silently returned empty caches. Switched the URL builder to the canonical
refs/heads/{branch}form so the GitHub Contents API path is built deterministically (#407). - TRaSH Guides fetch errors lost critical context, making issue #406 hard to triage. Four error sites in
github-fetcher.tsnow emit pino-structuredlog.x({ err, url, ... }, message)entries with the URL named in both the human-readable message and the merge object (#408). - Older Plex analytics helpers were leaking up to 5 100-character
sessionsJsonslices into pino logs on JSON-parse failure (watch-history,codec-analytics,device-analytics,user-analytics,quality-score). All five helpers stop populatingfailedPreviewsand gain the sameArray.isArrayguard PR #382 added (closes #383). - Library page now honors
?quality=and?service=deep links from Pulse notifications and dashboard service cards.useLibraryFiltersnow seeds from URL params on cold mount and re-syncs on warm client-side navigations (closes #404).
Notes for operators
- The Auto-Tagger writes to the *source-side arr (Sonarr/Radarr); pair with a Label Sync rule if you want the tag mirrored to Plex/Jellyfin/Emby labels.
- The webhook is exposed at
/api/auto-tag/webhook/:instanceIdas a public route (no session cookie required); auth is the per-user Bearer token. Rotate the secret via the panel if you suspect leakage — old token is invalidated immediately. - Trakt list-membership rules require
TRAKT_CLIENT_IDto be set in the API process env (issued fromtrakt.tv/oauth/applications). Without it, the Trakt scheduler logs a one-line "skipped — TRAKT_CLIENT_ID not configured" notice and trakt rules will not match. TMDb list-membership has no operator-side env requirement.
Full changelog: CHANGELOG.md · Compare: v2.17.0...v2.18.0