diff --git a/crates/arr-api/src/search.rs b/crates/arr-api/src/search.rs index 36cf87f..8745d56 100644 --- a/crates/arr-api/src/search.rs +++ b/crates/arr-api/src/search.rs @@ -3,8 +3,8 @@ use std::time::UNIX_EPOCH; use arr_core::policy::{evaluate, Candidate}; use arr_core::{ - DolbyVisionProfile, HdrRules, Language, MovieOverrides, Policy, PolicyId, RequiredAudio, - Resolution, Rule, ScoreWeights, SizeBand, Source, Verdict, + DolbyVisionProfile, HdrRules, Language, Policy, PolicyId, RequiredAudio, Resolution, Rule, + ScoreWeights, SizeBand, Source, TitleOverrides, Verdict, }; use arr_indexer::{ProwlarrClient, SearchRelease, SearchRequest}; use axum::extract::{Query, State}; @@ -293,7 +293,7 @@ pub async fn releases( })?; let overrides: OverridesJson = serde_json::from_value(movie.overrides) .map_err(|error| ApiError::Database(error.to_string()))?; - let overrides = MovieOverrides { + let overrides = TitleOverrides { only_4k: overrides.only_4k, allow_english_audio: overrides.allow_english_audio, }; @@ -460,7 +460,7 @@ fn policy_from_row(row: PolicyRow) -> Result { fn classify( release: SearchRelease, policy: &Policy, - overrides: &MovieOverrides, + overrides: &TitleOverrides, original_language: &Language, ) -> Result { let parsed = arr_parse::parse(&release.name); diff --git a/crates/arr-core/src/lib.rs b/crates/arr-core/src/lib.rs index 91bcffe..3449d2d 100644 --- a/crates/arr-core/src/lib.rs +++ b/crates/arr-core/src/lib.rs @@ -163,10 +163,8 @@ pub struct TitleOverrides { pub allow_english_audio: bool, } -pub type MovieOverrides = TitleOverrides; - #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum MovieState { +pub enum MediaState { Missing, Downloading, Available, @@ -181,8 +179,8 @@ pub struct Movie { pub original_language: Language, pub root_id: RootId, pub wanted: bool, - pub overrides: MovieOverrides, - pub state: MovieState, + pub overrides: TitleOverrides, + pub state: MediaState, pub blocked: bool, pub search_attempts: u32, pub last_searched_at: Option, @@ -210,13 +208,6 @@ pub struct Season { pub tracked: bool, } -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum EpisodeState { - Missing, - Downloading, - Available, -} - #[derive(Clone, Debug, Eq, PartialEq)] pub struct Episode { pub id: EpisodeId, @@ -225,7 +216,7 @@ pub struct Episode { pub title: String, pub air_date: Option, pub wanted: bool, - pub state: EpisodeState, + pub state: MediaState, pub search_attempts: u32, pub last_searched_at: Option, } diff --git a/crates/arr-core/src/policy.rs b/crates/arr-core/src/policy.rs index 800947d..cd3e2e0 100644 --- a/crates/arr-core/src/policy.rs +++ b/crates/arr-core/src/policy.rs @@ -2,8 +2,8 @@ use arr_parse::{LanguageMarker, NameClaims}; use crate::{ lang::{is_resolved_portuguese, language_of_marker}, - HdrFormat, Language, MovieOverrides, Policy, ProbedMedia, RequiredAudio, Resolution, Rule, - Source, Verdict, + HdrFormat, Language, Policy, ProbedMedia, RequiredAudio, Resolution, Rule, Source, + TitleOverrides, Verdict, }; /// The evidence available while evaluating a candidate. @@ -55,7 +55,7 @@ pub enum EvaluationPhase { #[derive(Clone, Copy, Debug)] pub struct EvaluationContext<'a> { pub policy: &'a Policy, - pub overrides: &'a MovieOverrides, + pub overrides: &'a TitleOverrides, /// The title's original language from TMDB (`DESIGN.md` §5.2) — a /// distinct input from any track's language. pub original_language: &'a Language, @@ -103,7 +103,7 @@ pub struct Evaluation { #[must_use] pub fn evaluate( policy: &Policy, - overrides: &MovieOverrides, + overrides: &TitleOverrides, original_language: &Language, candidate: Candidate<'_>, size_bytes: Option, @@ -514,7 +514,7 @@ mod tests { } } - fn verdict(policy: &Policy, overrides: &MovieOverrides, candidate: Candidate<'_>) -> Verdict { + fn verdict(policy: &Policy, overrides: &TitleOverrides, candidate: Candidate<'_>) -> Verdict { evaluate(policy, overrides, &en(), candidate, None).verdict } @@ -525,7 +525,7 @@ mod tests { ) -> Verdict { evaluate( policy, - &MovieOverrides::default(), + &TitleOverrides::default(), original_language, candidate, None, @@ -539,7 +539,7 @@ mod tests { let claims = claims(None, None); let report = evaluate( &policy, - &MovieOverrides::default(), + &TitleOverrides::default(), &en(), Candidate::PreGrab(&claims), None, @@ -562,7 +562,7 @@ mod tests { #[test] fn preferred_resolutions_pass_in_both_phases() { let policy = policy(); - let overrides = MovieOverrides::default(); + let overrides = TitleOverrides::default(); for claimed in [ClaimedResolution::P2160, ClaimedResolution::P1080] { let claims = claims(Some(claimed), Some(ClaimedSource::WebDl)); @@ -583,7 +583,7 @@ mod tests { #[test] fn resolution_outside_root_policy_hard_fails_in_both_phases() { let policy = policy(); - let overrides = MovieOverrides::default(); + let overrides = TitleOverrides::default(); let cases = [ (ClaimedResolution::P480, Resolution::Other(480)), (ClaimedResolution::P576, Resolution::Other(576)), @@ -607,9 +607,9 @@ mod tests { #[test] fn only_4k_tightens_the_root_policy_in_both_phases() { let policy = policy(); - let overrides = MovieOverrides { + let overrides = TitleOverrides { only_4k: true, - ..MovieOverrides::default() + ..TitleOverrides::default() }; let claims_1080 = claims(Some(ClaimedResolution::P1080), Some(ClaimedSource::WebDl)); let media = probed(Resolution::R1080p, Some(Source::WebDl)); @@ -634,9 +634,9 @@ mod tests { fn only_4k_never_expands_the_root_policy() { let mut policy = policy(); policy.resolution_preference = vec![Resolution::R1080p]; - let overrides = MovieOverrides { + let overrides = TitleOverrides { only_4k: true, - ..MovieOverrides::default() + ..TitleOverrides::default() }; let claims = claims(Some(ClaimedResolution::P2160), Some(ClaimedSource::WebDl)); @@ -649,7 +649,7 @@ mod tests { #[test] fn every_unsafe_source_hard_fails_in_both_phases() { let policy = policy(); - let overrides = MovieOverrides::default(); + let overrides = TitleOverrides::default(); let cases = [ (ClaimedSource::Cam, Source::Cam), (ClaimedSource::Telesync, Source::Telesync), @@ -679,7 +679,7 @@ mod tests { assert_eq!( verdict( &policy, - &MovieOverrides::default(), + &TitleOverrides::default(), Candidate::PreGrab(&claims), ), Verdict::Rejected(Rule::Source(Source::Telesync)) @@ -689,7 +689,7 @@ mod tests { #[test] fn every_safe_source_passes_in_both_phases() { let policy = policy(); - let overrides = MovieOverrides::default(); + let overrides = TitleOverrides::default(); let cases = [ (ClaimedSource::Dvd, Source::Dvd), (ClaimedSource::Telecine, Source::Telecine), @@ -721,7 +721,7 @@ mod tests { let media = probed(Resolution::R2160p, source); let report = evaluate( &policy, - &MovieOverrides::default(), + &TitleOverrides::default(), &en(), Candidate::PostDownload(&media), None, @@ -777,7 +777,7 @@ mod tests { let report = evaluate( policy, - &MovieOverrides::default(), + &TitleOverrides::default(), &en(), Candidate::PostDownload(&media), None, @@ -796,7 +796,7 @@ mod tests { let claims = arr_parse::parse("Movie.2024.2160p.WEB-DL.DV-GROUP"); let report = evaluate( &policy(), - &MovieOverrides::default(), + &TitleOverrides::default(), &en(), Candidate::PreGrab(&claims), None, @@ -823,7 +823,7 @@ mod tests { #[test] fn hard_failure_wins_over_an_earlier_soft_failure() { let policy = policy(); - let overrides = MovieOverrides::default(); + let overrides = TitleOverrides::default(); let original = en(); let claims = claims(None, None); let context = EvaluationContext { @@ -851,7 +851,7 @@ mod tests { #[test] fn first_failure_of_the_deciding_kind_is_stable() { let policy = policy(); - let overrides = MovieOverrides::default(); + let overrides = TitleOverrides::default(); let original = en(); let claims = claims(None, None); let context = EvaluationContext { @@ -984,9 +984,9 @@ mod tests { #[test] fn kids_allow_english_audio_grabs_and_imports_with_a_waiver() { let policy = kids_policy(); - let overrides = MovieOverrides { + let overrides = TitleOverrides { allow_english_audio: true, - ..MovieOverrides::default() + ..TitleOverrides::default() }; let claims = name_claims(&[LanguageMarker::English]); assert_eq!( @@ -1078,7 +1078,7 @@ mod tests { // kids: could be the required pt-PT — must not reject as a dub. let report = evaluate( &kids_policy(), - &MovieOverrides::default(), + &TitleOverrides::default(), &en(), Candidate::PostDownload(&media), None, @@ -1089,7 +1089,7 @@ mod tests { // as verified either. let report = evaluate( &policy(), - &MovieOverrides::default(), + &TitleOverrides::default(), &Language::PortugueseBrazil, Candidate::PostDownload(&media), None, @@ -1102,7 +1102,7 @@ mod tests { let claims = name_claims(&[LanguageMarker::Portuguese]); let report = evaluate( &kids_policy(), - &MovieOverrides::default(), + &TitleOverrides::default(), &en(), Candidate::PreGrab(&claims), None, diff --git a/crates/arr-core/src/score.rs b/crates/arr-core/src/score.rs index e1cb689..2e0e088 100644 --- a/crates/arr-core/src/score.rs +++ b/crates/arr-core/src/score.rs @@ -156,7 +156,7 @@ mod tests { use super::*; use crate::{ policy::{evaluate, EvaluationContext, PolicyRule, RuleEvaluation, SizeRule}, - HdrRules, Language, MovieOverrides, PolicyId, RequiredAudio, Rule, Verdict, + HdrRules, Language, PolicyId, RequiredAudio, Rule, TitleOverrides, Verdict, }; const GIB: u64 = 1 << 30; @@ -221,7 +221,7 @@ mod tests { fn size_rule(size_bytes: u64) -> RuleEvaluation { let policy = policy(); - let overrides = MovieOverrides::default(); + let overrides = TitleOverrides::default(); let language = Language::Other("en".to_owned()); let claims = claims(ClaimedSource::WebDl); SizeRule.evaluate(&EvaluationContext { @@ -256,7 +256,7 @@ mod tests { let claims = claims(ClaimedSource::Remux); let evaluation = evaluate( &policy, - &MovieOverrides::default(), + &TitleOverrides::default(), &Language::Other("en".to_owned()), Candidate::PreGrab(&claims), Some(gib(60)), diff --git a/crates/arr-db/migrations/0005_series.sql b/crates/arr-db/migrations/0005_series.sql index 55b064b..5490879 100644 --- a/crates/arr-db/migrations/0005_series.sql +++ b/crates/arr-db/migrations/0005_series.sql @@ -65,7 +65,8 @@ CREATE TABLE episodes ( title TEXT NOT NULL, air_date TEXT, wanted INTEGER NOT NULL DEFAULT 0 CHECK (wanted IN (0, 1)), - -- Mirrors arr-core's canonical EpisodeState vocabulary. + -- Mirrors arr-core's canonical MediaState vocabulary. The older movies + -- table uses legacy names; its additive correction is tracked in #73. state TEXT NOT NULL DEFAULT 'missing' CHECK (state IN ('missing', 'downloading', 'available')), search_attempts INTEGER NOT NULL DEFAULT 0, @@ -91,6 +92,7 @@ INSERT INTO policies ( json('{"dv_profile_allow":["8.1"],"dv_profile_reject":["5","7"],"allow_hdr10":true,"allow_hdr10plus":true,"allow_sdr":true}'), json('{"2160p":{"floor_gib":8,"target_gib":22,"penalty_points_per_gib_over":60},"1080p":{"floor_gib":3,"target_gib":8,"penalty_points_per_gib_over":60}}'), json('["2160p","1080p"]'), + -- §5.5 deliberately keeps source tier as Remux > BluRay > WEB-DL. json('{"Remux":4,"BluRay":3,"WEB-DL":2,"WEBRip":1,"HDTV":0}'), json('{"size_at_target":1000,"source_tier":25,"seeder_doubling":8}') ); diff --git a/crates/arr-db/src/lib.rs b/crates/arr-db/src/lib.rs index c7a69a9..665fc2d 100644 --- a/crates/arr-db/src/lib.rs +++ b/crates/arr-db/src/lib.rs @@ -360,7 +360,7 @@ mod tests { .bind(season_id) .execute(db.pool()) .await - .expect_err("database mirrors EpisodeState"); + .expect_err("database mirrors MediaState"); sqlx::query("UPDATE series SET upstream_ended = 2 WHERE id = ?") .bind(series_id) .execute(db.pool())