diff --git a/Cargo.lock b/Cargo.lock index d17bd6e..04eb9ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,6 +94,7 @@ dependencies = [ "arr-meta", "arr-parse", "arr-probe", + "arr-subs", "axum", "base64", "chrono", diff --git a/crates/arr-daemon/Cargo.toml b/crates/arr-daemon/Cargo.toml index 8f0c2d8..fe50d8e 100644 --- a/crates/arr-daemon/Cargo.toml +++ b/crates/arr-daemon/Cargo.toml @@ -20,6 +20,7 @@ arr-indexer = { workspace = true } arr-meta = { workspace = true } arr-parse = { workspace = true } arr-probe = { workspace = true } +arr-subs = { workspace = true } axum = { workspace = true } chrono = { workspace = true } include_dir = { workspace = true } diff --git a/crates/arr-daemon/src/main.rs b/crates/arr-daemon/src/main.rs index 34c45c9..09ec5f3 100644 --- a/crates/arr-daemon/src/main.rs +++ b/crates/arr-daemon/src/main.rs @@ -144,7 +144,13 @@ async fn run() -> Result<(), Error> { if let Some(tmdb_url) = config.tmdb_url { upstreams = upstreams.with_tmdb_url(tmdb_url); } - let state = AppState::new(upstreams)?.with_database(database.clone()); + let state = AppState::new(upstreams)? + .with_database(database.clone()) + .with_subtitle_providers(subtitle_providers( + config.opensubtitles_api_key, + config.opensubtitles_username, + config.opensubtitles_password, + )); let app = arr_api::router(state.clone()) .merge(arr_compat::router(compat)) @@ -443,3 +449,42 @@ async fn shutdown() { tracing::info!("shutting down"); } + +/// The subtitle providers this deployment can reach (DESIGN.md §15). +/// +/// Credentials are bootstrap config and never reach the database (§10), so +/// which providers *exist* is decided here, once, at startup; which of them a +/// search *runs* is the `providers_enabled` setting the API reads per +/// request. OpenSubtitles.com needs a registered API key to be called at all, +/// so without one it is not offered; Podnapisi is anonymous and always is. +/// +/// Translation backends are not wired: none is implemented yet (#191-#193), +/// and `AppState` defaults to an empty registry, which makes a manual +/// translation answer "no such engine" rather than fail obscurely. +fn subtitle_providers( + opensubtitles_api_key: Option, + username: Option, + password: Option, +) -> Vec> { + let mut providers: Vec> = Vec::new(); + + match arr_subs::Podnapisi::new() { + Ok(podnapisi) => providers.push(std::sync::Arc::new(podnapisi)), + Err(error) => tracing::warn!(%error, "Podnapisi not available"), + } + + let Some(api_key) = opensubtitles_api_key else { + tracing::info!("no OpenSubtitles.com API key configured; that provider is off"); + return providers; + }; + match arr_subs::OpenSubtitles::new(arr_subs::OpenSubtitlesConfig { + api_key, + username, + password, + }) { + Ok(opensubtitles) => providers.push(std::sync::Arc::new(opensubtitles)), + Err(error) => tracing::warn!(%error, "OpenSubtitles.com not available"), + } + + providers +}