Amend DESIGN.md with a subtitles section #183
Closed
opened 2026-08-24 21:28:31 +01:00 by naps62-yolo
·
1 comment
No Branch/Tag Specified
main
ui-mobile-first
codex/fix-action-button-reload
translation
qbit-2
docker
bugs
blitz/subtitles
fix/255-provider-reorder
blitz/feedback-3
subtitles/251-icon-vocab
subtitles/237-relocate-subs
fix/248-title-move-creates-root
blitz/feedback-2
issue/227-abandoned-pack-visible
issue/245-backoff-anchor
issue/240-design-coherence
issue/211-waived-rule
issue/232-plainer-words
issue/246-policy-reclassify
issue/244-root-slash-stored
issue/231-settings-rows
issue/241-reclassify-on-root-change
issue/239-failure-window
issue/230-icon-only
issue/243-root-path-edges
issue/238-attention-liveness
issue/229-back-button
issue/236-root-path-move
issue/226-attention-threshold
issue/228-root-move
blitz/size-bands
size-bands/209-runtime-scale
size-bands/210-size-waiver
size-bands/208-runtime-design
blitz/reliability
reliability/155-sqlx-migrations
reliability/176-refresh-on-add
removal-nav/175-removal-controls
removal-nav/174-season-file-removal
removal-nav/172-search-titles
removal-nav/173-homepage-library
removal-nav/171-untrack-clears
removal-nav/170-chip-align
removal-nav/169-design-amendment
feedback/167-deck-autosearch
feedback/165-chip-colour
feedback/166-header-rail
feedback/163-drop-cast
feedback/164-control-geometry
feedback/162-season-ordering
feedback/161-search-episodes
feedback/160-autotrack-seed
feedback/159-design-amend
blitz/rich-metadata
rich/151-grid
rich/148-search-rows
rich/150-series-meta
rich/149-movie-page
rich/147-search-art
rich/156-rating-null
rich/146-meta-api
rich/145-poster-cols
rich/144-trailer
rich/143-meta-detail
rich/142-design-amend
blitz/153-empty-episode-titles
blitz/152-deck-routes
blitz/141-season-vanished-api
blitz/129-series-detail-view
blitz/140-attention-episode-numbers
blitz/137-season-removals
blitz/133-attention-tv-lanes
blitz/139-movie-id-episode-guard
blitz/134-stale-attention-queue
blitz/124-rss-episode-matching
blitz/136-imdb-series-lookup
blitz/132-manual-commands
blitz/128-series-delete-files
blitz/131-status-seasons-input
blitz/138-plural-season-range
blitz/135-pack-name-parse
blitz/122-upstream-removals
blitz/130-add-series-search
blitz/121-series-refresh
blitz/127-unified-search-tv
blitz/123-episode-matching
blitz/126-tv-attention
blitz/119-tracked-rule
blitz/125-season-deck
blitz/120-tvdb-id
blitz/118-season-zero
blitz/117-design-amend
issue/115-avail-search
issue/112-name-truncate
issue/111-score-colors
No results found.
Labels
Clear labels
area/api
area/ci
area/compat
area/core
area/daemon
area/db
area/dl
area/indexer
area/infra
area/meta
area/parse
area/probe
area/subs
area/web
difficulty/easy
difficulty/hard
difficulty/moderate
difficulty/trivial
phase/1-skeleton
phase/2-logic
phase/3-sourcing
phase/4-movies
phase/5-ui
phase/6-tv
phase/7-people
phase/8-compat
phase/9-subtitles
type/bug
type/chore
type/feature
type/test
arr-api
CI and test harness
arr-compat
arr-core
arr-daemon
arr-db
arr-dl
arr-indexer
workspace and tooling
arr-meta
arr-parse
arr-probe
arr-subs crate: providers, extraction, translation, sync
web/
bounded, obvious approach, few files
subtle correctness or cross-cutting
multiple files, judgement, an interface to design
one file, mechanical, no design decisions
workspace, CI, config, database, empty API and SPA
parsing and policy engine, pure, no network
TMDB and Prowlarr, read-only
movies end to end
search, buckets, library views, queues
seasons, episodes, tracking, derived status
owner tags and notifications
Jellyseerr shim
build order: subtitles, DESIGN.md §15
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Blocks
Reference: yolo/arr#183
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
DESIGN.md§13 lists subtitles as phase 9 and says they are out of scope forthe document. Every other issue in this milestone cites a section that does not
exist yet. Write it.
Add §15 Subtitles to
DESIGN.md, and update §13 item 9 to point at itinstead of saying "out of scope". The section records the decisions below,
which were settled with the operator during planning. It is the contract: an
issue that disagrees with it is wrong.
What §15 must state
Wanted set. Global, not per root. Two languages are separately wanted for
every media file: Portuguese — pt-PT preferred, pt-BR accepted — and English.
A file is satisfied for a language when a subtitle in it exists, embedded or as
a sidecar. This is deliberately unlike §5.2's audio rules, which attach to a
root; subtitles carry no blacklist there and none here.
Embedded tracks. An embedded subtitle track satisfies its language.
Text-format tracks (
subrip,ass,mov_text) are additionally extracted toa sidecar SRT, because an extracted track is a legal translation source.
Image-format tracks (PGS on BluRay, VobSub on DVD) carry bitmaps, not text:
they satisfy viewing but can never feed a translator, and arr does not OCR
them.
arr-probealready reports subtitle tracks with resolved languages; theformat is the new fact it must carry.
Providers. OpenSubtitles.com and Podnapisi, behind one trait.
Ranking. A
moviehashmatch wins outright. Then an exact release-namematch, then same release group or same source, then uploader rating and
download count as tiebreakers. Every rejected candidate names the rule that
killed it, so the manual view described in §9.3 works unchanged for subtitles.
Forced and SDH. A forced track covers only foreign-language lines and
on-screen signs; it never satisfies a want and arr never goes looking for one.
An SDH track is complete and satisfies, ranked below a plain subtitle.
Translation. When no provider has a wanted language, arr translates
immediately — there is no waiting window. The source is an existing subtitle:
a downloaded one, or one extracted from a text-format embedded track. Being
able to translate from an embedded track is a deliberate improvement on
Bazarr, which cannot.
Translation backends. Pluggable, each behind its own cargo feature:
an OpenAI-compatible HTTP endpoint, DeepL, Google Translate, and a generic
remote command driven by a configured template (
ssh box claude -pis oneinstance of that template, not a backend of its own). The engine in use is a
database setting, so switching does not need a rebuild when the feature is
compiled in. Subtitles are sent in batches of cues; a reply whose cue count or
numbering does not match the batch is rejected. Timing data never leaves arr.
No upgrade loop. Once a language is satisfied — by a machine translation
too — arr stops working on it. A real subtitle appearing later does not
replace anything. Replacement is a manual action from the UI. This is §5.4's
rule applied to subtitles.
On disk. Sidecars live next to the video inside the §7.4 title folder,
named
<video basename>.<lang>.srt, e.g.… - [2160p][WEB-DL][HDR10].pt-PT.srt.A machine translation carries an extra
.mtsegment:… [HDR10].pt-PT.mt.srt. That keeps §7.4's audit-by-lsproperty — with theapp stopped, the filename says which subtitles are machine-made. Folder-level
delete stays atomic because sidecars are inside the folder.
Sync.
alassruns on every fetched and every translated subtitle. It is asingle small binary invoked like
ffprobe, so it costs nothing at rest. Itreports no confidence value, so its output is accepted unless it is
implausible — a shift beyond 60 seconds, or cues lost — in which case the
unsynced original is kept and the file is flagged.
Configuration. Provider credentials and translator API keys are bootstrap
config or environment, per §10. Wanted languages, chosen engine, per-provider
enable and the daily budgets are database rows edited from
/settingswithouta restart.
Budgets. A token bucket per provider and per translator, with a configured
daily allowance. The reconcile loop spends it newest-import-first, so enabling
this on an existing library drains the backlog over days instead of hitting
every rate limit at once. Being at the cap is a visible queue state, not an
error.
Notifications. No new event classes. §9.5 stands: subtitle fetches never
notify, and a provider or translator being unreachable folds into the existing
"Broken" message to the operator alone.
Non-goals, stated in §15 so they do not creep back: OCR of image-based
tracks, transcribing audio when no subtitle exists anywhere, adopting subtitle
files already on disk that arr did not write (§2 already says the service knows
only what it put there — which does mean arr may fetch a second copy alongside
one Bazarr left), and any background loop that upgrades a subtitle in place.
Done when
DESIGN.mdcarries §15 with all of the above, §13 item 9 points at it, and thecrate list in §11 and
CLAUDE.mdboth mentionarr-subs.Added §15 Subtitles to
DESIGN.md, recording every decision from this issue: the global two-language wanted set (pt-PT preferred / pt-BR accepted, plus English), embedded-track satisfaction with text-format extraction to sidecar SRT, the OpenSubtitles.com + Podnapisi provider trait, the moviehash-first ranking with named rejection rules, forced/SDH handling, immediate translation with pluggable feature-gated backends (OpenAI-compatible, DeepL, Google Translate, remote-command template), the no-upgrade rule,<basename>.<lang>[.mt].srtsidecar naming inside the §7.4 folder, alass sync with the implausibility guard, §10-style configuration split, per-provider/translator daily token buckets spent newest-import-first, no new notification classes, and the stated non-goals. §13 item 9 now points at §15 instead of "out of scope", andarr-subsis listed in §11 and inCLAUDE.md's repo layout.Branch:
subtitles/183-design-subsjust ci: skipped deliberately — the change is Markdown only (DESIGN.md,CLAUDE.md), nothing the gate checks is affected, and the machine is shared.Findings
None. The issue's decisions fit the existing document cleanly; no conflicts with §2, §5.2, §5.4, §7.4, §9.3, §9.5 or §10 turned up while cross-reading them.