feat(core): add movie domain types (#47)
This commit was merged in pull request #47.
This commit is contained in:
+284
-8
@@ -1,14 +1,290 @@
|
|||||||
//! arr-core — domain types, policy engine, scoring. See DESIGN.md §4 and §5.
|
//! Pure domain types shared by the arr crates.
|
||||||
//!
|
//!
|
||||||
//! This crate must never depend on `axum`, `sqlx` or `reqwest`. It holds the
|
//! This crate must never depend on `axum`, `sqlx` or `reqwest`.
|
||||||
//! logic that is tested constantly and it has to stay fast to compile.
|
|
||||||
|
use std::{collections::BTreeMap, path::PathBuf, time::SystemTime};
|
||||||
|
|
||||||
|
macro_rules! id_type {
|
||||||
|
($name:ident) => {
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct $name(pub i64);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
id_type!(RootId);
|
||||||
|
id_type!(PolicyId);
|
||||||
|
id_type!(MovieId);
|
||||||
|
id_type!(MediaFileId);
|
||||||
|
id_type!(ReleaseId);
|
||||||
|
id_type!(GrabId);
|
||||||
|
id_type!(OwnerId);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Audience {
|
||||||
|
Main,
|
||||||
|
Kids,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Root {
|
||||||
|
pub id: RootId,
|
||||||
|
pub audience: Audience,
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub policy_id: PolicyId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub enum Language {
|
||||||
|
PortuguesePortugal,
|
||||||
|
PortugueseBrazil,
|
||||||
|
PortugueseUnverified,
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub enum Resolution {
|
||||||
|
R2160p,
|
||||||
|
R1080p,
|
||||||
|
R720p,
|
||||||
|
Other(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub enum Source {
|
||||||
|
Remux,
|
||||||
|
BluRay,
|
||||||
|
WebDl,
|
||||||
|
WebRip,
|
||||||
|
Hdtv,
|
||||||
|
Telesync,
|
||||||
|
Cam,
|
||||||
|
Screener,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct DolbyVisionProfile {
|
||||||
|
pub profile: u8,
|
||||||
|
pub compatibility_id: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum HdrFormat {
|
||||||
|
Sdr,
|
||||||
|
Hdr10,
|
||||||
|
Hdr10Plus,
|
||||||
|
Hlg,
|
||||||
|
DolbyVision(DolbyVisionProfile),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum HdrClaim {
|
||||||
|
Hdr10,
|
||||||
|
Hdr10Plus,
|
||||||
|
Hlg,
|
||||||
|
DolbyVision,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum RequiredAudio {
|
||||||
|
OriginalLanguage,
|
||||||
|
AnyOf(Vec<Language>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct HdrRules {
|
||||||
|
pub rejected_dolby_vision_profiles: Vec<DolbyVisionProfile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct SizeBand {
|
||||||
|
pub floor_bytes: u64,
|
||||||
|
pub target_bytes: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Policy {
|
||||||
|
pub id: PolicyId,
|
||||||
|
pub name: String,
|
||||||
|
pub required_audio: RequiredAudio,
|
||||||
|
pub dub_blacklist: Vec<Language>,
|
||||||
|
pub hdr_rules: HdrRules,
|
||||||
|
pub size_bands: BTreeMap<Resolution, SizeBand>,
|
||||||
|
pub resolution_preference: Vec<Resolution>,
|
||||||
|
pub source_weights: BTreeMap<Source, i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct MovieOverrides {
|
||||||
|
pub only_4k: bool,
|
||||||
|
pub allow_english_audio: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum MovieState {
|
||||||
|
Missing,
|
||||||
|
Downloading,
|
||||||
|
Available,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Movie {
|
||||||
|
pub id: MovieId,
|
||||||
|
pub tmdb_id: u64,
|
||||||
|
pub title: String,
|
||||||
|
pub year: u16,
|
||||||
|
pub original_language: Language,
|
||||||
|
pub root_id: RootId,
|
||||||
|
pub wanted: bool,
|
||||||
|
pub overrides: MovieOverrides,
|
||||||
|
pub state: MovieState,
|
||||||
|
pub blocked: bool,
|
||||||
|
pub search_attempts: u32,
|
||||||
|
pub last_searched_at: Option<SystemTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct AudioTrack {
|
||||||
|
pub language: Language,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub handler_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct SubtitleTrack {
|
||||||
|
pub language: Language,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct ProbedMedia {
|
||||||
|
pub resolution: Resolution,
|
||||||
|
pub source: Option<Source>,
|
||||||
|
pub hdr: HdrFormat,
|
||||||
|
pub audio_tracks: Vec<AudioTrack>,
|
||||||
|
pub subtitle_tracks: Vec<SubtitleTrack>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Rule {
|
||||||
|
RequiredAudio,
|
||||||
|
DubBlacklist(Language),
|
||||||
|
DolbyVisionProfile(DolbyVisionProfile),
|
||||||
|
Resolution(Resolution),
|
||||||
|
Source(Source),
|
||||||
|
Size,
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Verdict {
|
||||||
|
Eligible,
|
||||||
|
Waived(Rule),
|
||||||
|
Rejected(Rule),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct MediaFile {
|
||||||
|
pub id: MediaFileId,
|
||||||
|
pub movie_id: MovieId,
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub size: u64,
|
||||||
|
pub probed: ProbedMedia,
|
||||||
|
pub waiver: Option<Rule>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct ParsedRelease {
|
||||||
|
pub resolution: Option<Resolution>,
|
||||||
|
pub source: Option<Source>,
|
||||||
|
pub hdr: Vec<HdrClaim>,
|
||||||
|
pub audio_languages: Vec<Language>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Release {
|
||||||
|
pub id: ReleaseId,
|
||||||
|
pub indexer_id: i64,
|
||||||
|
pub guid: String,
|
||||||
|
pub name: String,
|
||||||
|
pub size: u64,
|
||||||
|
pub seeders: u32,
|
||||||
|
pub publish_date: SystemTime,
|
||||||
|
pub download_url: String,
|
||||||
|
pub parsed: ParsedRelease,
|
||||||
|
pub score: i64,
|
||||||
|
pub verdict: Verdict,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum GrabState {
|
||||||
|
Downloading,
|
||||||
|
Downloaded,
|
||||||
|
Imported,
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Grab {
|
||||||
|
pub id: GrabId,
|
||||||
|
pub release_id: ReleaseId,
|
||||||
|
pub movie_id: MovieId,
|
||||||
|
pub infohash: String,
|
||||||
|
pub state: GrabState,
|
||||||
|
pub grabbed_at: SystemTime,
|
||||||
|
pub imported_at: Option<SystemTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct BlacklistEntry {
|
||||||
|
pub infohash: String,
|
||||||
|
pub normalised_name: String,
|
||||||
|
pub reason: String,
|
||||||
|
pub created_at: SystemTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Owner {
|
||||||
|
pub id: OwnerId,
|
||||||
|
pub name: String,
|
||||||
|
pub ntfy_topic: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct MovieOwner {
|
||||||
|
pub movie_id: MovieId,
|
||||||
|
pub owner_id: OwnerId,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
/// Placeholder so the test gate is meaningful on an empty workspace —
|
use super::{DolbyVisionProfile, HdrFormat, Language, Rule, Verdict};
|
||||||
/// `cargo nextest` fails a run with zero tests, and relaxing that would
|
|
||||||
/// let a broken filter go green later. Replaced by real tests in the
|
|
||||||
/// domain-types and policy-engine issues.
|
|
||||||
#[test]
|
#[test]
|
||||||
fn workspace_builds_and_tests_run() {}
|
fn portuguese_variants_are_distinct() {
|
||||||
|
assert_ne!(Language::PortuguesePortugal, Language::PortugueseBrazil);
|
||||||
|
assert_ne!(Language::PortugueseBrazil, Language::PortugueseUnverified);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dolby_vision_profile_is_preserved() {
|
||||||
|
assert_ne!(
|
||||||
|
HdrFormat::DolbyVision(DolbyVisionProfile {
|
||||||
|
profile: 8,
|
||||||
|
compatibility_id: Some(1),
|
||||||
|
}),
|
||||||
|
HdrFormat::DolbyVision(DolbyVisionProfile {
|
||||||
|
profile: 8,
|
||||||
|
compatibility_id: Some(2),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_eligible_verdicts_name_the_rule() {
|
||||||
|
let rule = Rule::DolbyVisionProfile(DolbyVisionProfile {
|
||||||
|
profile: 5,
|
||||||
|
compatibility_id: None,
|
||||||
|
});
|
||||||
|
assert_eq!(Verdict::Rejected(rule.clone()), Verdict::Rejected(rule));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user