Wire translation backends into the daemon #216

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

arr-daemon/src/main.rs builds AppState with with_subtitle_providers but
never calls with_translation_backends. The registry #190 defined and
#191/#192/#193 fill is therefore always empty in the real binary, so
POST /api/media-files/{id}/subtitles/translate answers 503 on every request
no matter which translate-* features the binary was compiled with.

Tests do not catch it because they build AppState themselves and inject a
stub backend, so the endpoint is well covered and still dead in production.

Wire the compiled-in backends from the daemon, selected by the
translation_engine database setting (#198) and configured from the
bootstrap keys that already exist (ARR_TRANSLATE_OPENAI_*,
ARR_TRANSLATE_DEEPL_API_KEY, ARR_TRANSLATE_GOOGLE_API_KEY, and the
remote-command template). arr_subs::compiled_engines() already reports which
ones this build can offer.

Found while implementing #214.

Depends on: #191, #192, #193, #198

`arr-daemon/src/main.rs` builds `AppState` with `with_subtitle_providers` but never calls `with_translation_backends`. The registry #190 defined and #191/#192/#193 fill is therefore always empty in the real binary, so `POST /api/media-files/{id}/subtitles/translate` answers 503 on every request no matter which `translate-*` features the binary was compiled with. Tests do not catch it because they build `AppState` themselves and inject a stub backend, so the endpoint is well covered and still dead in production. Wire the compiled-in backends from the daemon, selected by the `translation_engine` database setting (#198) and configured from the bootstrap keys that already exist (`ARR_TRANSLATE_OPENAI_*`, `ARR_TRANSLATE_DEEPL_API_KEY`, `ARR_TRANSLATE_GOOGLE_API_KEY`, and the remote-command template). `arr_subs::compiled_engines()` already reports which ones this build can offer. Found while implementing #214. Depends on: #191, #192, #193, #198
naps62-yolo added this to the Subtitles milestone 2026-08-25 01:50:43 +01:00
naps62-yolo added the area/daemontype/bugdifficulty/moderatephase/9-subtitles labels 2026-08-25 01:50:43 +01:00
Author
Owner

Wired the compiled-in translation backends into arr-daemon. Two things were needed beyond the daemon-side plumbing: arr-daemon/Cargo.toml never turned on any of arr-subs's translate-* features on its dependency, so arr_subs::compiled_engines() was empty in the real binary regardless of wiring — added matching pass-through features (translate-openai, translate-deepl, translate-google, translate-command, all off by default, same as arr-subs). crates/arr-daemon/src/main.rs now has a translation_backends(&Config) function, built the same way subtitle_providers already is: a backend is offered when its feature is compiled in and it has what it needs to construct (OpenAI is offered whenever compiled in, since its API key is optional; DeepL, Google and the remote command backend need their credential/template present). AppState::with_translation_backends is now called from api_state. Which of the offered backends actually runs a given translation stays the translation_engine database setting, read per request — unchanged.

Branch: subtitles/216-wire-backends (based on blitz/subtitles), pushed.

just ci passed: 653 tests, fmt/clippy/machete/web-check all green.

Findings

  • No config source exists for the OpenAI model. OpenAi::new/with_base_url take the model as a constructor argument (per #191, intentionally not a field of OpenAiConfig), but neither the bootstrap config nor the subtitle_settings table (#198) has anywhere to put it — #198's migration only added translation_engine, no model field. Without a value the backend can't be constructed at all, so I added ARR_TRANSLATE_OPENAI_MODEL / translate_openai_model as a new non-secret bootstrap key (same file/env precedence as translate_openai_base_url), defaulting to gpt-4o-mini. This is a stopgap: worth deciding, in a follow-up issue, whether the model belongs in bootstrap config (fixed per deployment) or should be a subtitle_settings row like translation_engine is (editable without a restart, per §15's "Configuration" paragraph).
  • remote_command_timeout_seconds (in subtitle_settings, editable from /settings) never reaches the Command backend. The backend's CommandConfig.timeout is set once at construction (I used arr_subs::COMMAND_DEFAULT_TIMEOUT); there's no path from the database row to a running backend instance, so editing that setting from the UI currently has no effect until an issue wires it through — contradicts §15's "database rows edited from /settings without a restart".
Wired the compiled-in translation backends into `arr-daemon`. Two things were needed beyond the daemon-side plumbing: `arr-daemon/Cargo.toml` never turned on any of arr-subs's `translate-*` features on its dependency, so `arr_subs::compiled_engines()` was empty in the real binary regardless of wiring — added matching pass-through features (`translate-openai`, `translate-deepl`, `translate-google`, `translate-command`, all off by default, same as arr-subs). `crates/arr-daemon/src/main.rs` now has a `translation_backends(&Config)` function, built the same way `subtitle_providers` already is: a backend is offered when its feature is compiled in and it has what it needs to construct (OpenAI is offered whenever compiled in, since its API key is optional; DeepL, Google and the remote command backend need their credential/template present). `AppState::with_translation_backends` is now called from `api_state`. Which of the offered backends actually runs a given translation stays the `translation_engine` database setting, read per request — unchanged. Branch: `subtitles/216-wire-backends` (based on `blitz/subtitles`), pushed. `just ci` passed: 653 tests, fmt/clippy/machete/web-check all green. ## Findings - **No config source exists for the OpenAI model.** `OpenAi::new`/`with_base_url` take the model as a constructor argument (per #191, intentionally not a field of `OpenAiConfig`), but neither the bootstrap config nor the `subtitle_settings` table (#198) has anywhere to put it — #198's migration only added `translation_engine`, no model field. Without a value the backend can't be constructed at all, so I added `ARR_TRANSLATE_OPENAI_MODEL` / `translate_openai_model` as a new non-secret bootstrap key (same file/env precedence as `translate_openai_base_url`), defaulting to `gpt-4o-mini`. This is a stopgap: worth deciding, in a follow-up issue, whether the model belongs in bootstrap config (fixed per deployment) or should be a `subtitle_settings` row like `translation_engine` is (editable without a restart, per §15's "Configuration" paragraph). - **`remote_command_timeout_seconds` (in `subtitle_settings`, editable from `/settings`) never reaches the `Command` backend.** The backend's `CommandConfig.timeout` is set once at construction (I used `arr_subs::COMMAND_DEFAULT_TIMEOUT`); there's no path from the database row to a running backend instance, so editing that setting from the UI currently has no effect until an issue wires it through — contradicts §15's "database rows edited from `/settings` without a restart".
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yolo/arr#216