Language-aware Newznab enrichment proxy for the Arr stack, focused on Danish audio and subtitle detection.
DKSubs Proxy sits between Radarr/Sonarr and Prowlarr. It enriches Newznab search results with stable release markers (.DKOK for Danish subs, .DKaudio for Danish audio) so Arr custom formats can correctly prefer Danish releases without relying only on weak indexer titles. Designed for private media-server workflows where Prowlarr indexer metadata is inconsistent and Radarr/Sonarr custom-format matching needs stronger language signals.
Combines multiple signals to detect Danish content:
- Title scanning — instantly tags releases with
NORDiC,DANISH,DANSK, scene conventions (free, zero I/O) - Newznab attribute parsing — reads
subs=Danish/language=Danishattrs when indexers expose them (free) - Extended-attr enrichment — bypasses Prowlarr's attr-stripping by querying indexers directly with
extended=1 - NFO/MediaInfo deep scan — downloads NFO files for releases that don't advertise Danish in the title; catches multi-language UHD remuxes with hidden Danish tracks
- v5.6 caching/coordination layers — request dedup with single-flight lock, movie-level "no Danish available" verdicts, cross-indexer release-name dedup, per-indexer hit-rate budget scaling, NFO early-exit, 30-day negative cache. Together: ~86% reduction in indexer API calls vs naive probing.
The proxy does not replace Prowlarr. It wraps Prowlarr and returns enriched Newznab-compatible XML back to Radarr and Sonarr.
Current format (bracketless, scene-style suffix):
| Tag | Meaning |
|---|---|
.DKaudio |
Danish audio detected (from title, attribute, or NFO) |
.DKOK |
Danish subtitles detected (from title, attribute, or NFO) |
Custom-format regexes are backward-compatible with the older bracket forms ([DK:Title], [DKAudio:Title], [DK-NFO], [DKAudio.Title] etc.) so files already on disk continue to score correctly.
sudo git clone https://github.com/unknown0152/dksubs-proxy-v2.git /root/cosmos-deploy/DK
sudo bash /root/cosmos-deploy/DK/setup.shThat's it. setup.sh detects what's running and does the right things:
- Fresh box → deploys nzbdav, sets up rclone FUSE mount, wires it into Radarr/Sonarr, configures everything
- Box on altmount/qBittorrent → offers to migrate (auto-imports Usenet providers from altmount), then wires nzbdav, removes the old containers
- Already running nzbdav → skips deployment, just refreshes CFs/profiles/indexer cats/timers
- Out-of-date rclone → upgrades to a version that supports nzbdav's WebDAV symlinks
For updates: cd /root/cosmos-deploy/DK && sudo git pull && sudo bash setup.sh
The proxy can ALSO parse already-fetched NFO text for media properties (Dolby Vision, HDR10/+, Atmos, TrueHD, DTS-HD MA, Remux) and append proxy-owned .NFOxxx tokens to release titles alongside the existing .DKOK / .DKaudio tag. No extra HTTP/API calls — it piggybacks on the same NFO text the proxy already fetches for DK detection. Default OFF.
Enable on the proxy (in /srv/config/dksubs-proxy/.env):
DKSUBS_PROXY_NFO_MEDIA_TAGS=1Then restart the container.
Apply the matching Custom Formats in Radarr/Sonarr with conservative starter scores:
curl -fsSL https://raw.githubusercontent.com/unknown0152/dksubs-proxy-v2/master/add-nfo-cfs.py | sudo python3 -Or if you cloned the repo:
sudo python3 /root/cosmos-deploy/DK/add-nfo-cfs.pyThis creates 7 Custom Formats (NFO: Dolby Vision, NFO: HDR10+, NFO: HDR10, NFO: Atmos, NFO: TrueHD, NFO: DTS-HD MA, NFO: Remux) in both arrs and assigns them small bonus scores (+75 to +200) to the existing DanishAudio / EnglishSubs profiles. The helper is idempotent — re-running just updates existing scores in place.
Rollback:
sudo python3 /root/cosmos-deploy/DK/add-nfo-cfs.py --rollback # scores → 0, CFs remain
sudo python3 /root/cosmos-deploy/DK/add-nfo-cfs.py --delete # remove CFs entirelySee PR #4 for the full scoring rationale.
- Radarr + Sonarr (and their
-4kcounterparts when present):DKAudioandDKSubscustom formats (scored 10000, regex matches both legacy bracket tags AND new bracketless suffixes)- Audio codec CFs as tiebreakers: TrueHD Atmos (+2000), DTS-X (+1800), TrueHD (+1600), DTS-HD MA (+1400), EAC3 Atmos (+1200), EAC3 (+1000), DTS (+500), AAC (+100)
DanishAudioandEnglishSubsquality profiles (minFormatScore=10000, cutoff=Remux-2160p, upgrades enabled)- Low qualities disabled (CAM/TS/SDTV/DVD/480p/576p/HDTV-720p)
- Quality Definitions: sane min/preferred/max MB/min for 720p/1080p/2160p
- Indexer categories trimmed (SD/Other/3D/DVD dropped)
- Prowlarr:
- All indexers renamed
<name> {DK}and rerouted viahttp://dksubs-proxy:9699/<id> - Application syncCategories trimmed; syncLevel set to
addOnlyso subsequent syncs don't undo the rewire
- All indexers renamed
- nzbdav:
- Container deployed on
media-stacknetwork with/srv/config/nzbdav+/mnt+/srv/media(orMEDIA_DIR) mounts - Admin account, WebDAV creds, Usenet providers, and
api.keywritten directly to the SQLite DB (no web-UI step) - Providers auto-imported from existing
/srv/config/altmount/config.yamlif present
- Container deployed on
- Host:
- Latest rclone installed if Debian's apt version (1.60) is too old
rclone-nzbdav.servicesystemd unit mounting nzbdav's WebDAV at/mnt/nzbdavwith--linksarr-force-import.timerrunning every 1 min — force-imports any Radarr/Sonarr queue items stuck inimportPending/importBlocked(the workaround for nzbdav's obfuscated-NZB hash filenames)
- dksubs-proxy:
- Container on
media-stack,.envwith auto-detected indexer routing +DROP_NON_DK=1filter mode + per-category min release sizes
- Container on
Radarr/Sonarr → dksubs-proxy:9699/{indexer_id} → Prowlarr → NZB Indexer
↓
[Scans titles + fetches NFOs]
↓
Returns tagged RSS to Radarr/Sonarr
NFO fetching uses two stages per release:
- Prowlarr's
t=getnfoproxy (usually returns error 202 — most indexers don't support it via Prowlarr) - Direct call to the indexer's own API using real API keys extracted from Prowlarr's database
Background model: The proxy returns partial results (title hits + fast NFO fetches) within 5 seconds. Slow NFO fetches continue in the background and populate the cache for the next poll.
All variables are written to .env by setup-proxy.sh. You can override them manually:
| Variable | Default | Description |
|---|---|---|
PROWLARR_URL |
http://Prowlarr:9696 |
Prowlarr internal URL |
PROWLARR_API_KEY |
(required) | Prowlarr API key |
NFO_INDEXERS |
auto-detected | Comma-separated Prowlarr indexer IDs for NFO scanning |
INDEXER_{id}_APIKEY |
auto | Real API key for indexer (from Prowlarr's SQLite DB) |
INDEXER_{id}_BASEURL |
auto | Base URL for indexer |
NFO_TIMEOUT |
5.0 |
Foreground NFO fetch budget in seconds |
NFO_DIRECT_RATE_CALLS |
8 |
Max direct NFO API calls per rate window |
NFO_DIRECT_RATE_WINDOW |
10 |
Rate limit window in seconds |
CACHE_TTL_NEGATIVE |
2592000 |
Seconds before a "no Danish" result is retried (30d, extended from 6h in v5.6 — NZB IDs are immutable) |
DEBUG_LOGGING |
0 |
Set to 1 for verbose logs |
# Live logs
docker logs -f dksubs-proxy
# Health check
curl http://dksubs-proxy:9699/health
# Metrics (JSON)
curl http://dksubs-proxy:9699/metricsMetrics include: requests_total, hunt_total, dk_hits, nfo_fetches, nfo_direct_fetches, nfo_direct_hits, cache_hits, cache_misses.
bash setup-proxy.sh && docker compose up -dDirect NFO API calls are rate-limited per indexer (default: 8 requests/10s). For indexers with stricter limits, add to .env:
INDEXER_{id}_RATE_CALLS=5
INDEXER_{id}_RATE_WINDOW=10
For indexers with very strict limits (e.g. 300 calls/5 min):
INDEXER_{id}_RATE_CALLS=250
INDEXER_{id}_RATE_WINDOW=300
The .DKOK and .DKaudio release markers are considered stable wire-format markers. They are intentionally kept short and unchanged so existing Radarr/Sonarr custom formats continue to work across releases. The Custom Format regexes Radarr/Sonarr use also remain backward-compatible with legacy bracketed forms ([DK:Title], [DKAudio:Title], [DK-NFO], [DKAudio.Title], etc.) so any files already on disk continue to score correctly.
The project name, Docker image (ghcr.io/unknown0152/dksubs-proxy), environment variables (DKSUBS_PROXY_V56_FEATURES, DKSUBS_REGEX, etc.), Cosmos Market manifest, and container/service names all remain under the dksubs-proxy naming scheme for production compatibility. This is intentional, not legacy debt — renaming would cascade through GHCR, env, Cosmos, and deployed containers on multiple hosts with no functional benefit.
MIT — built for the home-lab community.