fix(api): drop subtitle queue gaps for unwanted languages
An attempt row survives after a language leaves wanted_languages — missing_for (#201) already bounds by the current wanted set, so the queue reads the same way instead of showing a stale gap forever. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1272,9 +1272,13 @@ async fn refresh_jellyfin(state: &AppState) {
|
||||
)
|
||||
)]
|
||||
pub async fn queue(State(state): State<AppState>) -> Result<Json<SubtitleQueue>, ApiError> {
|
||||
// A language dropped from the wanted set (§15) is not a gap any more —
|
||||
// its attempt row just has not been cleaned up yet. `missing_for` (#201)
|
||||
// already applies this bound; the queue reads the same way.
|
||||
let wanted: BTreeSet<String> = wanted_languages(&state).await?.into_iter().collect();
|
||||
Ok(Json(SubtitleQueue {
|
||||
movies: movie_gaps(&state).await?,
|
||||
series: series_gaps(&state).await?,
|
||||
movies: movie_gaps(&state, &wanted).await?,
|
||||
series: series_gaps(&state, &wanted).await?,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1295,7 +1299,10 @@ fn attempt_reason(
|
||||
}
|
||||
}
|
||||
|
||||
async fn movie_gaps(state: &AppState) -> Result<Vec<MovieSubtitleGaps>, ApiError> {
|
||||
async fn movie_gaps(
|
||||
state: &AppState,
|
||||
wanted: &BTreeSet<String>,
|
||||
) -> Result<Vec<MovieSubtitleGaps>, ApiError> {
|
||||
let database = pool(state)?;
|
||||
let mut out: Vec<MovieSubtitleGaps> = Vec::new();
|
||||
|
||||
@@ -1313,6 +1320,9 @@ async fn movie_gaps(state: &AppState) -> Result<Vec<MovieSubtitleGaps>, ApiError
|
||||
.fetch_all(database)
|
||||
.await?;
|
||||
for row in attempts {
|
||||
if !wanted.contains(&row.language) {
|
||||
continue;
|
||||
}
|
||||
let (reason, detail) = attempt_reason(row.state, row.last_failure);
|
||||
push_movie_gap(
|
||||
&mut out,
|
||||
@@ -1388,7 +1398,10 @@ fn push_movie_gap(
|
||||
});
|
||||
}
|
||||
|
||||
async fn series_gaps(state: &AppState) -> Result<Vec<SeriesSubtitleGaps>, ApiError> {
|
||||
async fn series_gaps(
|
||||
state: &AppState,
|
||||
wanted: &BTreeSet<String>,
|
||||
) -> Result<Vec<SeriesSubtitleGaps>, ApiError> {
|
||||
let database = pool(state)?;
|
||||
let mut out: Vec<SeriesSubtitleGaps> = Vec::new();
|
||||
|
||||
@@ -1410,6 +1423,9 @@ async fn series_gaps(state: &AppState) -> Result<Vec<SeriesSubtitleGaps>, ApiErr
|
||||
.fetch_all(database)
|
||||
.await?;
|
||||
for row in attempts {
|
||||
if !wanted.contains(&row.language) {
|
||||
continue;
|
||||
}
|
||||
let (reason, detail) = attempt_reason(row.state, row.last_failure);
|
||||
push_episode_gap(
|
||||
&mut out,
|
||||
@@ -2710,6 +2726,29 @@ mod tests {
|
||||
assert!(body["series"].as_array().expect("series").is_empty());
|
||||
}
|
||||
|
||||
/// #202: an attempt row for a language no longer in the wanted set is
|
||||
/// not a gap any more — `missing_for` (#201) already draws this line.
|
||||
#[tokio::test]
|
||||
async fn subtitle_queue_drops_a_language_no_longer_wanted() {
|
||||
let fixture = stub_application().await;
|
||||
arr_db::subtitles::record_attempt(
|
||||
&fixture.pool,
|
||||
fixture.media_file_id,
|
||||
"en",
|
||||
arr_db::SubtitleState::Failed,
|
||||
Some("429 from opensubtitles"),
|
||||
)
|
||||
.await
|
||||
.expect("record failed attempt");
|
||||
sqlx::query("UPDATE subtitle_settings SET wanted_languages = '[\"pt-PT\"]' WHERE id = 1")
|
||||
.execute(&fixture.pool)
|
||||
.await
|
||||
.expect("narrow wanted set");
|
||||
|
||||
let body = queue(&fixture.base).await;
|
||||
assert!(body["movies"].as_array().expect("movies").is_empty());
|
||||
}
|
||||
|
||||
/// #202: a movie's failed attempt surfaces with its language and detail.
|
||||
#[tokio::test]
|
||||
async fn subtitle_queue_lists_a_movie_gap() {
|
||||
|
||||
Reference in New Issue
Block a user