Import pipeline: probe, hardlink, rename, layout (#82)
This commit was merged in pull request #82.
This commit is contained in:
@@ -13,12 +13,12 @@
|
||||
//! - the `grabs` row is written from that response, so a crash between the add
|
||||
//! and the insert heals on the next tick instead of leaving an orphan.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use arr_core::policy::{evaluate, Candidate};
|
||||
use arr_core::{score::score, Language, Policy, Rule, TitleOverrides, Verdict};
|
||||
use arr_core::{score::score, Language, Policy, TitleOverrides, Verdict};
|
||||
use arr_db::{Db, MoviePolicy};
|
||||
use arr_dl::{AddTorrent, TorrentSource, TransmissionClient};
|
||||
use arr_indexer::{ProwlarrClient, SearchRelease, SearchRequest};
|
||||
@@ -317,7 +317,14 @@ impl GrabAction {
|
||||
let candidates = self
|
||||
.search(database, movie, indexers, &loaded, &original_language)
|
||||
.await?;
|
||||
let Some(winner) = candidates.into_iter().next() else {
|
||||
// §6.3: anything that hard-failed post-ffprobe is never grabbed
|
||||
// again. The same release reappears under new infohashes, so the key
|
||||
// is the normalised name.
|
||||
let blacklisted = blacklisted_names(database).await?;
|
||||
let Some(winner) = candidates
|
||||
.into_iter()
|
||||
.find(|candidate| !blacklisted.contains(&arr_parse::normalise(&candidate.name)))
|
||||
else {
|
||||
tracing::info!(
|
||||
movie_id = movie.id,
|
||||
title = movie.title,
|
||||
@@ -558,6 +565,17 @@ async fn store_release(
|
||||
}))
|
||||
}
|
||||
|
||||
/// Every blacklist key (§6.3), for filtering candidates. Household scale: a
|
||||
/// handful of rows, refetched per title rather than cached.
|
||||
async fn blacklisted_names(database: &Db) -> Result<HashSet<String>, GrabError> {
|
||||
let names = sqlx::query_scalar!(
|
||||
r#"SELECT normalised_name AS "normalised_name!: String" FROM blacklist"#
|
||||
)
|
||||
.fetch_all(database.pool())
|
||||
.await?;
|
||||
Ok(names.into_iter().collect())
|
||||
}
|
||||
|
||||
/// Record that the title was searched, so the next tick takes a different one.
|
||||
async fn record_search(database: &Db, movie_id: i64) -> Result<(), GrabError> {
|
||||
sqlx::query!(
|
||||
@@ -605,20 +623,7 @@ fn verdict_columns(verdict: &Verdict) -> (&'static str, Option<String>) {
|
||||
match verdict {
|
||||
Verdict::Eligible => ("eligible", None),
|
||||
Verdict::Waived(_) => ("waived", None),
|
||||
Verdict::Rejected(rule) => ("rejected", Some(rule_name(rule))),
|
||||
}
|
||||
}
|
||||
|
||||
fn rule_name(rule: &Rule) -> String {
|
||||
match rule {
|
||||
Rule::RequiredAudio => "required_audio".into(),
|
||||
Rule::DubBlacklist(_) => "dub_blacklist".into(),
|
||||
Rule::PortugueseUnverified => "portuguese_unverified".into(),
|
||||
Rule::DolbyVisionProfile(_) => "dolby_vision_profile".into(),
|
||||
Rule::Resolution(_) => "resolution".into(),
|
||||
Rule::Source(_) => "source".into(),
|
||||
Rule::Size => "size".into(),
|
||||
Rule::Other(name) => name.clone(),
|
||||
Verdict::Rejected(rule) => ("rejected", Some(rule.name())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -990,6 +995,33 @@ mod tests {
|
||||
assert!(fake.torrents().is_empty());
|
||||
}
|
||||
|
||||
/// §6.3: a hard-failed release is never grabbed again, even when it is
|
||||
/// still the best-scoring candidate — the next one down wins instead.
|
||||
#[tokio::test]
|
||||
async fn a_blacklisted_release_is_passed_over() {
|
||||
let (_dir, database) = wanted_movie().await;
|
||||
sqlx::query("INSERT INTO blacklist (infohash, normalised_name, reason) VALUES (?, ?, ?)")
|
||||
.bind("ffff")
|
||||
.bind(arr_parse::normalise(
|
||||
"Dune.Part.Two.2024.2160p.WEB-DL.DDP5.1.Atmos",
|
||||
))
|
||||
.bind("dolby_vision_profile")
|
||||
.execute(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
let indexer = prowlarr().await;
|
||||
let (downloader, fake) = transmission().await;
|
||||
|
||||
action(&indexer, &downloader).tick(&database).await.unwrap();
|
||||
|
||||
assert_eq!(fake.torrents().len(), 1);
|
||||
assert!(
|
||||
fake.torrents()[0].source.ends_with("huge.torrent"),
|
||||
"the remux wins once the WEB-DL is blacklisted: {}",
|
||||
fake.torrents()[0].source
|
||||
);
|
||||
}
|
||||
|
||||
/// §6.3: `blocked` stops targeted search for a title.
|
||||
#[tokio::test]
|
||||
async fn a_blocked_title_is_not_searched() {
|
||||
|
||||
Reference in New Issue
Block a user