Hard/soft fail handling and release blacklist (#87)
This commit was merged in pull request #87.
This commit is contained in:
@@ -203,7 +203,7 @@ impl ImportAction {
|
||||
|
||||
let Some(content) = self.transmission.torrent_content(&pending.infohash).await? else {
|
||||
// Gone from Transmission. Whether that is a failure or a manual
|
||||
// removal is issue #24's call; leave the grab alone.
|
||||
// removal is issue #86's call; leave the grab alone.
|
||||
tracing::warn!(
|
||||
grab_id = pending.grab_id,
|
||||
infohash = pending.infohash,
|
||||
@@ -312,16 +312,12 @@ impl ImportAction {
|
||||
pending: &PendingImport,
|
||||
reason: &str,
|
||||
) -> Result<Outcome, ImportError> {
|
||||
let normalised = arr_parse::normalise(&pending.release_name);
|
||||
sqlx::query!(
|
||||
"INSERT INTO blacklist (infohash, normalised_name, reason)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT (infohash) DO NOTHING",
|
||||
pending.infohash,
|
||||
normalised,
|
||||
reason
|
||||
arr_db::blacklist::add(
|
||||
database.pool(),
|
||||
Some(&pending.infohash),
|
||||
&pending.release_name,
|
||||
reason,
|
||||
)
|
||||
.execute(database.pool())
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"UPDATE grabs SET state = 'failed' WHERE id = ?",
|
||||
@@ -770,6 +766,60 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// §5.7 soft fail: watchable but not what was asked. It imports, and the
|
||||
/// row carries the relaxed rule — the file must never read as a clean
|
||||
/// match. The torrent is untouched either way (§7.3).
|
||||
#[tokio::test]
|
||||
async fn an_english_only_kids_import_carries_a_waiver() {
|
||||
let h = harness(HDR10_PROBE).await;
|
||||
// The kids policy requires Portuguese audio; `allow_english_audio`
|
||||
// turns that hard fail into a waiver (§5.2, §5.7).
|
||||
let kids_library = h.library.join("kids");
|
||||
std::fs::create_dir_all(&kids_library).unwrap();
|
||||
sqlx::query("UPDATE roots SET path = ? WHERE kind = 'movie' AND audience = 'kids'")
|
||||
.bind(kids_library.to_string_lossy().into_owned())
|
||||
.execute(h.database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query(
|
||||
r#"UPDATE movies
|
||||
SET root_id = (SELECT id FROM roots
|
||||
WHERE kind = 'movie' AND audience = 'kids'),
|
||||
overrides = '{"allow_english_audio":true}'
|
||||
WHERE id = 1"#,
|
||||
)
|
||||
.execute(h.database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let outcomes = h.action.tick(&h.database).await.unwrap();
|
||||
|
||||
assert_eq!(outcomes.len(), 1);
|
||||
let waiver: Option<String> = sqlx::query_scalar(
|
||||
"SELECT json_extract(waiver, '$.rule') FROM media_files
|
||||
WHERE owner_kind = 'movie' AND owner_id = 1",
|
||||
)
|
||||
.fetch_one(h.database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(waiver.as_deref(), Some("required_audio"));
|
||||
|
||||
let blacklisted: i64 = sqlx::query_scalar("SELECT count(*) FROM blacklist")
|
||||
.fetch_one(h.database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(blacklisted, 0, "a soft fail blacklists nothing");
|
||||
let grab_state: String = sqlx::query_scalar("SELECT state FROM grabs")
|
||||
.fetch_one(h.database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(grab_state, "imported");
|
||||
assert!(
|
||||
h.downloads.join("Dune/Dune.mkv").is_file(),
|
||||
"§7.3: neither failure mode deletes the torrent"
|
||||
);
|
||||
}
|
||||
|
||||
/// §8: killed between the hardlink and the bookkeeping, a restart
|
||||
/// converges instead of failing on the existing destination.
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user