The Podnapisi zip fixtures never reached the branch: a global gitignore
excludes *.zip, so git add skipped them silently and the worker's CI passed
against untracked files. Recovered, with a fixtures .gitignore that keeps
the next binary fixture from vanishing the same way.
Two providers landing in parallel also disagreed on names and on which
crate dependencies each integration-test target uses. PodnapisiProvider is
now Podnapisi, matching OpenSubtitles, and both test targets declare the
dependencies they do not use so the per-target lint stays quiet.
Searches by moviehash computed from the media file and by TMDB id
with season/episode for TV, ranks candidates through
arr_core::subs::rank, and downloads under a lazily-fetched user
token. The daily download cap (429/406) surfaces as Error::RateLimited
so the loop can show a queue state. Credentials come from config or
environment only; tests run against wiremock fixtures.
The Backend trait plus everything the backends (#191-#193) share, so no
backend can skip it: chunking into character-budgeted batches, rejection
of replies whose cue count or numbering drifted, and reassembly onto the
original timings. Timing data never leaves arr; pt-PT and pt-BR are
distinct targets a backend must refuse rather than conflate.
Translation reassembles translated text onto original timings (§15), so
cues need a structured form. Parsing is tolerant of real files (CRLF,
BOM, missing indices, dot milliseconds); rendering is strict and
renumbers from 1.
#184 and #185 were built in parallel and their candidate types did not
meet. Ranking returned no identity for a candidate, so the winner of a
rank() could not be handed back to Provider::download -- rank() reorders,
so the caller could not recover it by position either.
RankedSubtitle now carries the index of the candidate in the slice it was
given, and Candidate::to_core is the one place the two shapes are mapped:
hash_match against a compared hash, sdh against hearing_impaired, group
against release_group, and the two optional tiebreakers defaulted to sort
last rather than block a candidate.
The skeleton the subtitles milestone hangs off: provider domain types, an
object-safe Provider trait, the crate's own error type, and the cargo
features the translation backends will sit behind. No provider, no
translation, no ranking — DESIGN.md §15 keeps ranking pure in arr-core.