Fetch subtitles from OpenSubtitles.com #187
Closed
opened 2026-08-24 21:28:32 +01:00 by naps62-yolo
·
2 comments
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.
Reference: yolo/arr#187
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.
Implement the OpenSubtitles.com provider against the
Providertrait from#184. It is the primary provider: it has an official REST API, it distinguishes
pt-PT from pt-BR properly, and it supports
moviehashmatching, which is thesignal that wins ranking outright (DESIGN.md §15).
moviehashcomputed from the media file, and by TMDB id with theseason and episode numbers for TV, so both lanes feed the ranker.
group, source, uploader rating, download count, forced flag, SDH flag.
is #195's job, not this crate's.
database.
429when the cap is hit. Surfacethat as a distinct error the loop can recognise, rather than a generic
transport failure — #197 turns it into a visible queue state.
Never point tests at the live service. Fixture responses, as everywhere else in
this repo.
Depends on: #184
Integration note from the milestone driver, before you start.
This issue says "download a chosen candidate to a temporary path". The
Providertrait merged in #184 does not do that —downloadreturnsFetched { id, language, format, content: Vec<u8> }, the bytes in memory. That was a deliberate call and DESIGN.md §15 backs it:alassruns over the subtitle (#194) before it gets a name and a place in the title folder (#195), so a provider that wrote temp files would also own cleaning them up.Implement against the merged trait. The issue wording is the stale half.
Also: rank the candidates you return via
arr_core::subs::rank, and useCandidate::to_corefor the conversion rather than buildingSubtitleCandidateyourself — that mapping is decided in one place now (cdd6133).Implemented the OpenSubtitles.com provider in
crates/arr-subs/src/opensubtitles.rsagainst theProvidertrait from #184. Search runs both lanes:moviehashcomputed from the media file's own bytes (64 KiB end chunks per the OpenSubtitles algorithm, dropped for files too small to have two distinct ends) and TMDB id with season/episode for TV. Candidates carry hash match, release name, group and source (read off the release name witharr_parse::parse, so ranking shares one parser), uploader rating, download count, forced (foreign_parts_only) and SDH (hearing_impaired) — and are returned ordered best-first througharr_core::subs::rankviaCandidate::to_core. Download logs in on demand (/loginbasic auth, token cached, one refresh on mid-flight 401), posts/downloadfor a file id, then fetches the link bytes intoFetched; download needs user credentials and answersError::Unauthorizedwithout them. The daily cap (429, and 406 as documented) surfaces asError::RateLimited { retry_after }for #197. Credentials arrive asOpenSubtitlesConfig— config/env shaped, never database; Debug impls are hand-written so keys and tokens never render. Tests are wiremock fixtures plus hand-derived hash vectors; nothing touches the live service.Branch:
subtitles/187-opensubtitles(based onblitz/subtitles).just ci: passed — 542 tests green through the gate (exit 0).Findings
Providertrait, which returns bytes in memory. The driver's integration note already resolves this in favour of the trait; implemented accordingly.Error::Iois raised when the media file cannot be opened for hashing, but files under 128 KiB silently search without the hash lane. That matches the API's own behaviour, but #196's loop may want to surface "too small to hash" somewhere visible.downloadcan fillFetchedfrom an opaque id. If the loop ever holds a candidate id across a restart, it must re-search before downloading or it getsNotFound.