Sync manually grabbed subtitles with alass #214

Closed
opened 2026-08-25 00:41:21 +01:00 by naps62-yolo · 1 comment
Owner

#199 shipped the manual grab endpoint before the two things a grab has to do
to the bytes it fetched existed on the branch.

The conversion half is done — srt_text in crates/arr-api/src/subtitles.rs
now calls Fetched::to_srt (26bd25e on blitz/subtitles), so a provider
serving VTT or ASS is converted rather than refused, and a format with no
parser fails before anything reaches the disk.

What remains is the sync half. A manually grabbed sidecar is still
recorded with sync: "not_run". Once #194 lands, run alass on the fetched
subtitle before the row is written, with the implausibility rules §15 states:
a shift beyond 60 seconds or lost cues keeps the unsynced original and flags
the file. #196 will call the same seam from the reconcile loop, so put it
somewhere both paths can reach rather than inline in the handler.

Depends on: #194, #199

#199 shipped the manual grab endpoint before the two things a grab has to do to the bytes it fetched existed on the branch. **The conversion half is done** — `srt_text` in `crates/arr-api/src/subtitles.rs` now calls `Fetched::to_srt` (26bd25e on `blitz/subtitles`), so a provider serving VTT or ASS is converted rather than refused, and a format with no parser fails before anything reaches the disk. **What remains is the sync half.** A manually grabbed sidecar is still recorded with `sync: "not_run"`. Once #194 lands, run `alass` on the fetched subtitle before the row is written, with the implausibility rules §15 states: a shift beyond 60 seconds or lost cues keeps the unsynced original and flags the file. #196 will call the same seam from the reconcile loop, so put it somewhere both paths can reach rather than inline in the handler. Depends on: #194, #199
naps62-yolo added this to the Subtitles milestone 2026-08-25 00:41:21 +01:00
naps62-yolo added the difficulty/moderatephase/9-subtitlestype/featurearea/api labels 2026-08-25 00:41:34 +01:00
naps62-yolo added a new dependency 2026-08-25 00:41:34 +01:00
naps62-yolo added a new dependency 2026-08-25 00:41:34 +01:00
naps62-yolo changed title from Wire conversion and sync into the manual grab to Sync manually grabbed subtitles with alass 2026-08-25 00:46:43 +01:00
Author
Owner

Implemented the sync half of the manual grab: POST /api/media-files/{id}/subtitles/grab now runs alass on the fetched subtitle before the row is recorded, per §15's implausibility rules (a shift beyond 60s or lost cues keeps the unsynced original and flags the file).

The seam lives in arr-subs (Syncer::settle, new SyncState/Settled types), not inlined in the handler, so #196's reconcile loop can call the same function when it lands: it runs Syncer::sync, folds an accepted result into the content to write back, an implausible result into keeping the original with a warning logged, and — since alass has no health check yet (#200) — a spawn/timeout/non-zero failure degrades to NotRun with a warning rather than failing the grab. AppState gained a Syncer (with_syncer, defaulting to alass on PATH); the daemon binary wires it from config.alass_path, which already existed from #194 but was unused until now.

Branch: subtitles/214-grab-sync, based on blitz/subtitles.

just ci: passed (642 tests).

Findings

  • translate() (the /subtitles/translate endpoint) still records every machine translation with sync: "not_run". §15 says alass runs on "every fetched and every translated subtitle," but #214's issue body scoped this to the manual grab endpoint only ("A manually grabbed sidecar is still recorded with sync: not_run... run alass on the fetched subtitle"), so I left it out rather than widening scope. Same Syncer::settle seam applies there unchanged — it's a small follow-up.
  • AppState::with_translation_backends is never called in arr-daemon/src/main.rs. The daemon wires subtitle_providers but not translation backends, so /subtitles/translate currently 503s on every request in production regardless of compiled-in cargo features. Looks like a gap from #190/#191/#192, not something this issue touches.
Implemented the sync half of the manual grab: `POST /api/media-files/{id}/subtitles/grab` now runs `alass` on the fetched subtitle before the row is recorded, per §15's implausibility rules (a shift beyond 60s or lost cues keeps the unsynced original and flags the file). The seam lives in `arr-subs` (`Syncer::settle`, new `SyncState`/`Settled` types), not inlined in the handler, so #196's reconcile loop can call the same function when it lands: it runs `Syncer::sync`, folds an accepted result into the content to write back, an implausible result into keeping the original with a warning logged, and — since alass has no health check yet (#200) — a spawn/timeout/non-zero failure degrades to `NotRun` with a warning rather than failing the grab. `AppState` gained a `Syncer` (`with_syncer`, defaulting to `alass` on `PATH`); the daemon binary wires it from `config.alass_path`, which already existed from #194 but was unused until now. Branch: `subtitles/214-grab-sync`, based on `blitz/subtitles`. `just ci`: passed (642 tests). ## Findings - `translate()` (the `/subtitles/translate` endpoint) still records every machine translation with `sync: "not_run"`. §15 says alass runs on "every fetched and every translated subtitle," but #214's issue body scoped this to the manual grab endpoint only ("A manually grabbed sidecar is still recorded with sync: not_run... run alass on the fetched subtitle"), so I left it out rather than widening scope. Same `Syncer::settle` seam applies there unchanged — it's a small follow-up. - `AppState::with_translation_backends` is never called in `arr-daemon/src/main.rs`. The daemon wires `subtitle_providers` but not translation backends, so `/subtitles/translate` currently 503s on every request in production regardless of compiled-in cargo features. Looks like a gap from #190/#191/#192, not something this issue touches.
naps62-yolo added a new dependency 2026-08-25 01:50:50 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: yolo/arr#214