Merge #196: close the subtitle gap in the reconcile loop

Closes #196
This commit is contained in:
Miguel Palhas
2026-08-25 02:20:11 +01:00
5 changed files with 1738 additions and 0 deletions
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO subtitle_attempts (media_file_id, language)\n SELECT id, ? FROM media_files WHERE probed IS NOT NULL\n ON CONFLICT (media_file_id, language) DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "35ace0417f613340f82522549d70d0dffeb680afb57511cb17fa15b3a571b974"
}
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT probed FROM media_files WHERE id = ?",
"describe": {
"columns": [
{
"name": "probed",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "probed"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
true
]
},
"hash": "af10ccd655f39235f525a531e38850b2e60ff713a038ee1e7c04c616b4613b52"
}
@@ -0,0 +1,50 @@
{
"db_name": "SQLite",
"query": "SELECT wanted_languages AS \"wanted_languages!: String\",\n providers_enabled AS \"providers_enabled!: String\",\n translation_engine AS \"translation_engine: String\"\n FROM subtitle_settings WHERE id = 1",
"describe": {
"columns": [
{
"name": "wanted_languages!: String",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "subtitle_settings",
"name": "wanted_languages"
}
}
},
{
"name": "providers_enabled!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "subtitle_settings",
"name": "providers_enabled"
}
}
},
{
"name": "translation_engine: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "subtitle_settings",
"name": "translation_engine"
}
}
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
true
]
},
"hash": "caa03a8daf9064de5c4724fcfecf70cc13f9a188468395bf5558aa3068c8a4e4"
}
+29
View File
@@ -13,6 +13,7 @@ mod reaper;
pub mod reconcile; pub mod reconcile;
mod rss; mod rss;
mod series_refresh; mod series_refresh;
mod subtitles;
mod tv_grab; mod tv_grab;
mod web; mod web;
@@ -33,6 +34,7 @@ use reaper::ReaperAction;
use reconcile::{ReconcileLoop, Tick}; use reconcile::{ReconcileLoop, Tick};
use rss::RssAction; use rss::RssAction;
use series_refresh::SeriesRefreshAction; use series_refresh::SeriesRefreshAction;
use subtitles::SubtitleAction;
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use tv_grab::TvGrabAction; use tv_grab::TvGrabAction;
@@ -331,6 +333,9 @@ fn reconcile_loop(
), ),
); );
// §15: subtitle gaps are reconciled from the same rows the API writes.
reconcile = reconcile.register(Tick::Reconcile, subtitle_action(config, notifier)?);
// §9.5 *needs a decision* and *broken* both go to the operator alone; // §9.5 *needs a decision* and *broken* both go to the operator alone;
// without a topic configured there is nowhere to send them. // without a topic configured there is nowhere to send them.
if let Some(operator_topic) = &config.ntfy_operator_topic { if let Some(operator_topic) = &config.ntfy_operator_topic {
@@ -474,6 +479,30 @@ fn jellyfin_client(config: &Config) -> Result<arr_api::jellyfin::JellyfinClient,
)?) )?)
} }
/// The §15 reconcile lane: closes subtitle gaps from the attempt rows.
///
/// Translation backends are passed empty: none is constructed at startup yet
/// — the compiled-in backends (#191#193) still need their bootstrap wiring
/// (model names, base URLs) — so the translate step records "not compiled"
/// and backs off rather than failing obscurely.
fn subtitle_action(config: &Config, notifier: &Notifier) -> Result<SubtitleAction, Error> {
let action = SubtitleAction::new(
subtitle_providers(
config.opensubtitles_api_key.clone(),
config.opensubtitles_username.clone(),
config.opensubtitles_password.clone(),
),
Vec::new(),
arr_subs::Syncer::new().with_binary(config.alass_path.clone()),
arr_probe::Extractor::new().with_binary(config.ffmpeg_path.clone()),
jellyfin_client(config)?,
);
Ok(match &config.ntfy_operator_topic {
Some(topic) => action.with_notifier(notifier.clone(), topic.clone()),
None => action,
})
}
/// The subtitle providers this deployment can reach (DESIGN.md §15). /// The subtitle providers this deployment can reach (DESIGN.md §15).
/// ///
/// Credentials are bootstrap config and never reach the database (§10), so /// Credentials are bootstrap config and never reach the database (§10), so
File diff suppressed because it is too large Load Diff