Decode downloaded subtitles to UTF-8 #212

Closed
opened 2026-08-24 22:36:47 +01:00 by naps62-yolo · 1 comment
Owner

Provider::download returns Fetched.content as Vec<u8> — raw bytes,
deliberately, because providers serve Latin-1 and Windows-1252 files and
guessing wrong at the provider boundary is worse than handing the bytes on
(#184). arr_subs::srt::parse deliberately takes &str (#190).

Nothing in the milestone closes that gap. Every downstream consumer needs
text: translation parses cues, alass needs a file it can read (#194), and
the sidecar written into the title folder has to be in a known encoding
(#195).

Decode once, in arr-subs, between download and everything else:

  • Honour a UTF-8 BOM when present.
  • Otherwise detect the encoding rather than assuming — chardetng or
    equivalent — and transcode to UTF-8.
  • Fail loudly when the bytes decode to nothing plausible, rather than
    writing mojibake into the library. A subtitle that cannot be decoded is a
    failed attempt (#186 already records a failure reason).

Sidecars on disk are UTF-8. That is what §15's "audit by ls" property is
worth nothing without: a file nobody can read is not a satisfied want.

Found while implementing #190.

Depends on: #184

`Provider::download` returns `Fetched.content` as `Vec<u8>` — raw bytes, deliberately, because providers serve Latin-1 and Windows-1252 files and guessing wrong at the provider boundary is worse than handing the bytes on (#184). `arr_subs::srt::parse` deliberately takes `&str` (#190). Nothing in the milestone closes that gap. Every downstream consumer needs text: translation parses cues, `alass` needs a file it can read (#194), and the sidecar written into the title folder has to be in a known encoding (#195). Decode once, in `arr-subs`, between download and everything else: - Honour a UTF-8 BOM when present. - Otherwise detect the encoding rather than assuming — `chardetng` or equivalent — and transcode to UTF-8. - Fail loudly when the bytes decode to nothing plausible, rather than writing mojibake into the library. A subtitle that cannot be decoded is a failed attempt (#186 already records a failure reason). Sidecars on disk are UTF-8. That is what §15's "audit by `ls`" property is worth nothing without: a file nobody can read is not a satisfied want. Found while implementing #190. Depends on: #184
naps62-yolo added this to the Subtitles milestone 2026-08-24 22:36:47 +01:00
naps62-yolo added the area/subsphase/9-subtitlestype/featuredifficulty/moderate labels 2026-08-24 22:36:47 +01:00
naps62-yolo added a new dependency 2026-08-24 22:37:11 +01:00
naps62-yolo added a new dependency 2026-08-24 22:37:27 +01:00
naps62-yolo added a new dependency 2026-08-24 22:37:27 +01:00
naps62-yolo added a new dependency 2026-08-24 22:37:28 +01:00
Author
Owner

Implemented: Fetched::decode() -> Result<String> in arr-subs (new decode module). It sniffs a UTF-8/UTF-16 BOM first via encoding_rs::Encoding::decode, and otherwise detects the encoding with chardetng and transcodes. Returns the new Error::Decode { candidate, detail } when the guessed encoding still can't decode the bytes cleanly, rather than writing mojibake. Added chardetng and encoding_rs as pinned workspace deps.

Branch: subtitles/212-decode-utf8.

just ci passed (531/531 tests, clippy and lint checks clean).

Findings

  • Nothing in the repo yet calls Provider::download or Fetched::decode outside tests — no live caller wired up. Expected: #194 (alass) and #195 (sidecar write) are the ones that will consume this.
Implemented: `Fetched::decode() -> Result<String>` in `arr-subs` (new `decode` module). It sniffs a UTF-8/UTF-16 BOM first via `encoding_rs::Encoding::decode`, and otherwise detects the encoding with `chardetng` and transcodes. Returns the new `Error::Decode { candidate, detail }` when the guessed encoding still can't decode the bytes cleanly, rather than writing mojibake. Added `chardetng` and `encoding_rs` as pinned workspace deps. Branch: `subtitles/212-decode-utf8`. `just ci` passed (531/531 tests, clippy and lint checks clean). ## Findings - Nothing in the repo yet calls `Provider::download` or `Fetched::decode` outside tests — no live caller wired up. Expected: #194 (alass) and #195 (sidecar write) are the ones that will consume this.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: yolo/arr#212