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> {
|
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 {
|
Ok(Json(SubtitleQueue {
|
||||||
movies: movie_gaps(&state).await?,
|
movies: movie_gaps(&state, &wanted).await?,
|
||||||
series: series_gaps(&state).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 database = pool(state)?;
|
||||||
let mut out: Vec<MovieSubtitleGaps> = Vec::new();
|
let mut out: Vec<MovieSubtitleGaps> = Vec::new();
|
||||||
|
|
||||||
@@ -1313,6 +1320,9 @@ async fn movie_gaps(state: &AppState) -> Result<Vec<MovieSubtitleGaps>, ApiError
|
|||||||
.fetch_all(database)
|
.fetch_all(database)
|
||||||
.await?;
|
.await?;
|
||||||
for row in attempts {
|
for row in attempts {
|
||||||
|
if !wanted.contains(&row.language) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let (reason, detail) = attempt_reason(row.state, row.last_failure);
|
let (reason, detail) = attempt_reason(row.state, row.last_failure);
|
||||||
push_movie_gap(
|
push_movie_gap(
|
||||||
&mut out,
|
&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 database = pool(state)?;
|
||||||
let mut out: Vec<SeriesSubtitleGaps> = Vec::new();
|
let mut out: Vec<SeriesSubtitleGaps> = Vec::new();
|
||||||
|
|
||||||
@@ -1410,6 +1423,9 @@ async fn series_gaps(state: &AppState) -> Result<Vec<SeriesSubtitleGaps>, ApiErr
|
|||||||
.fetch_all(database)
|
.fetch_all(database)
|
||||||
.await?;
|
.await?;
|
||||||
for row in attempts {
|
for row in attempts {
|
||||||
|
if !wanted.contains(&row.language) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let (reason, detail) = attempt_reason(row.state, row.last_failure);
|
let (reason, detail) = attempt_reason(row.state, row.last_failure);
|
||||||
push_episode_gap(
|
push_episode_gap(
|
||||||
&mut out,
|
&mut out,
|
||||||
@@ -2710,6 +2726,29 @@ mod tests {
|
|||||||
assert!(body["series"].as_array().expect("series").is_empty());
|
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.
|
/// #202: a movie's failed attempt surfaces with its language and detail.
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn subtitle_queue_lists_a_movie_gap() {
|
async fn subtitle_queue_lists_a_movie_gap() {
|
||||||
|
|||||||
Reference in New Issue
Block a user