fix(arr): drop the forced flag from subtitle grab
§15 gives forced tracks no sidecar name and never lets them satisfy a want; ranking already rejects every forced candidate (#222). A grab endpoint accepting `forced: true` had nowhere coherent to put the result, so the flag and its stale test are gone (#233).
This commit is contained in:
@@ -14,8 +14,10 @@
|
||||
//! handlers do the work inline and answer with the result, rather than
|
||||
//! returning 202 and leaving the operator to poll. The consequence the client
|
||||
//! has to know about: candidate ids are meaningful only to the provider that
|
||||
//! issued them, and a grab therefore repeats the facts (`forced`, `sdh`) the
|
||||
//! search reported, because nothing on the server remembers them.
|
||||
//! issued them, and a grab therefore repeats the fact (`sdh`) the search
|
||||
//! reported, because nothing on the server remembers it. A forced candidate
|
||||
//! is never eligible (§15, #185), so a grab has no `forced` flag to accept —
|
||||
//! there is nowhere to put one (issue #233).
|
||||
//!
|
||||
//! Verdict vocabulary is §9.3's, unchanged: `eligible` or `rejected` plus the
|
||||
//! name of the rule that killed it, exactly as `Release` spells it, so the
|
||||
@@ -282,11 +284,11 @@ pub struct SubtitleGrabInput {
|
||||
pub provider: String,
|
||||
pub candidate_id: String,
|
||||
pub language: String,
|
||||
/// The candidate's own flags, as the search reported them. Nothing on
|
||||
/// the server remembers a search, so they travel with the grab; they are
|
||||
/// facts about the subtitle and are stored with it.
|
||||
#[serde(default)]
|
||||
pub forced: bool,
|
||||
/// The candidate's own SDH flag, as the search reported it. Nothing on
|
||||
/// the server remembers a search, so it travels with the grab; it is a
|
||||
/// fact about the subtitle and is stored with it. No `forced` flag
|
||||
/// exists here: ranking rejects every forced candidate outright (§15),
|
||||
/// so a grab never has one to record (issue #233).
|
||||
#[serde(default)]
|
||||
pub sdh: bool,
|
||||
}
|
||||
@@ -989,20 +991,10 @@ pub async fn grab(
|
||||
&destination.to_string_lossy(),
|
||||
)
|
||||
.sync(db_sync_state(sync.state));
|
||||
if input.forced {
|
||||
record = record.forced();
|
||||
}
|
||||
if input.sdh {
|
||||
record = record.sdh();
|
||||
}
|
||||
finish(
|
||||
&state,
|
||||
&record,
|
||||
target.media_file_id,
|
||||
&language,
|
||||
input.forced,
|
||||
)
|
||||
.await
|
||||
finish(&state, &record, target.media_file_id, &language).await
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -1082,14 +1074,7 @@ pub async fn translate(
|
||||
&destination.to_string_lossy(),
|
||||
)
|
||||
.sync(db_sync_state(sync.state));
|
||||
finish(
|
||||
&state,
|
||||
&record,
|
||||
target.media_file_id,
|
||||
&target_language,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
finish(&state, &record, target.media_file_id, &target_language).await
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -1259,20 +1244,17 @@ async fn write_sidecar(destination: &Path, text: &str) -> Result<(), ApiError> {
|
||||
/// The `mark_satisfied` is §15's "manual actions bypass the wanted-set
|
||||
/// logic": the loop stops working on that language whether or not it was in
|
||||
/// the wanted set, so a manually requested Spanish subtitle is never treated
|
||||
/// as a gap and never replaced. A forced track is the exception the same
|
||||
/// section names — it covers signs only and satisfies nothing — so it is
|
||||
/// recorded and left out of the satisfaction claim.
|
||||
/// as a gap and never replaced. Every grab reaching this point is a plain or
|
||||
/// SDH subtitle — a forced candidate is never eligible, so there is no
|
||||
/// exception left to carve out of the satisfaction claim (issue #233).
|
||||
async fn finish(
|
||||
state: &AppState,
|
||||
record: &arr_db::NewSubtitleFile,
|
||||
media_file_id: i64,
|
||||
language: &Language,
|
||||
forced: bool,
|
||||
) -> Result<(StatusCode, Json<Subtitle>), ApiError> {
|
||||
let id = db::record_file(pool(state)?, record).await?;
|
||||
if !forced {
|
||||
db::mark_satisfied(pool(state)?, media_file_id, &language.to_string()).await?;
|
||||
}
|
||||
db::mark_satisfied(pool(state)?, media_file_id, &language.to_string()).await?;
|
||||
let subtitle = db::files_for(pool(state)?, media_file_id)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -2197,18 +2179,16 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// §15: a forced track covers signs only, so it is recorded and it still
|
||||
/// satisfies nothing.
|
||||
/// §233: the grab endpoint has no `forced` flag to accept — a forced
|
||||
/// candidate is never eligible (§15), so nothing sent it one.
|
||||
#[tokio::test]
|
||||
async fn a_forced_grab_is_recorded_without_satisfying_the_language() {
|
||||
async fn a_grab_has_no_forced_flag_to_send() {
|
||||
let fixture = stub_application().await;
|
||||
let mut body = pt();
|
||||
body["candidate_id"] = serde_json::json!("forced");
|
||||
body["forced"] = serde_json::json!(true);
|
||||
let body = pt();
|
||||
assert!(body.get("forced").is_none());
|
||||
let (status, subtitle) = grab(&fixture, body).await;
|
||||
assert_eq!(status, StatusCode::CREATED);
|
||||
assert_eq!(subtitle["forced"], true);
|
||||
assert_eq!(fixture.attempt_state("pt-PT").await, None);
|
||||
assert_eq!(subtitle["forced"], false);
|
||||
}
|
||||
|
||||
/// §15 has no in-place replacement: delete first, then fetch.
|
||||
|
||||
Reference in New Issue
Block a user