Skip to main content

ferrex_model/
media_events.rs

1use super::{LibraryId, Media, MediaID, MovieBatchId, MovieReference, Series};
2
3use crate::{
4    SeriesID, SubjectKey,
5    chrono::{DateTime, Utc},
6};
7
8use std::fmt;
9use uuid::Uuid;
10
11#[cfg(feature = "rkyv")]
12use crate::rkyv_wrappers::DateTimeWrapper;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[cfg_attr(
17    feature = "rkyv",
18    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
19)]
20#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, PartialEq, Eq)))]
21pub struct ScanStageLatencySummary {
22    pub scan: u64,
23    pub analyze: u64,
24    pub index: u64,
25}
26
27#[derive(Clone, PartialEq)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29#[cfg_attr(
30    feature = "rkyv",
31    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
32)]
33#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, PartialEq)))]
34pub struct ScanProgressEvent {
35    pub version: String,
36    pub scan_id: Uuid,
37    pub library_id: LibraryId,
38    pub status: String,
39    pub completed_items: u64,
40    pub total_items: u64,
41    pub sequence: u64,
42    #[cfg_attr(
43        feature = "serde",
44        serde(skip_serializing_if = "Option::is_none")
45    )]
46    pub current_path: Option<String>,
47    #[cfg_attr(
48        feature = "serde",
49        serde(skip_serializing_if = "Option::is_none")
50    )]
51    pub path_key: Option<SubjectKey>,
52    pub p95_stage_latencies_ms: ScanStageLatencySummary,
53    pub correlation_id: Uuid,
54    pub idempotency_key: String,
55    #[cfg_attr(feature = "rkyv", rkyv(with = DateTimeWrapper))]
56    pub emitted_at: DateTime<Utc>,
57    #[cfg_attr(
58        feature = "serde",
59        serde(skip_serializing_if = "Option::is_none")
60    )]
61    pub retrying_items: Option<u64>,
62    #[cfg_attr(
63        feature = "serde",
64        serde(skip_serializing_if = "Option::is_none")
65    )]
66    pub dead_lettered_items: Option<u64>,
67}
68
69impl fmt::Debug for ScanProgressEvent {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        f.debug_struct("ScanProgressEvent")
72            .field("scan_id", &self.scan_id)
73            .field("library_id", &self.library_id)
74            .field("status", &self.status)
75            .field("completed_items", &self.completed_items)
76            .field("total_items", &self.total_items)
77            .field("sequence", &self.sequence)
78            .field("current_path", &self.current_path)
79            .field("retrying_items", &self.retrying_items)
80            .field("dead_lettered_items", &self.dead_lettered_items)
81            .field("correlation_id", &self.correlation_id)
82            .field("idempotency_key", &self.idempotency_key)
83            .field("p95_stage_latencies_ms", &self.p95_stage_latencies_ms)
84            .field("emitted_at", &self.emitted_at)
85            .finish()
86    }
87}
88
89#[derive(Debug, Clone, PartialEq)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91#[cfg_attr(
92    feature = "rkyv",
93    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
94)]
95#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, PartialEq)))]
96pub struct ScanEventMetadata {
97    pub version: String,
98    pub correlation_id: Uuid,
99    pub idempotency_key: String,
100    pub library_id: LibraryId,
101}
102
103#[derive(Debug, Clone, PartialEq)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
105#[cfg_attr(
106    feature = "rkyv",
107    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
108)]
109#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "snake_case"))]
110#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, PartialEq)))]
111pub enum MediaEvent {
112    MovieAdded {
113        movie: MovieReference,
114    },
115    MovieBatchFinalized {
116        library_id: LibraryId,
117        batch_id: MovieBatchId,
118    },
119    SeriesAdded {
120        series: Series,
121    },
122    SeriesBundleFinalized {
123        library_id: LibraryId,
124        series_id: SeriesID,
125    },
126    MovieUpdated {
127        movie: MovieReference,
128    },
129    SeriesUpdated {
130        series: Series,
131    },
132
133    MediaDeleted {
134        id: MediaID,
135    },
136
137    ScanStarted {
138        scan_id: Uuid,
139        metadata: ScanEventMetadata,
140    },
141    ScanProgress {
142        scan_id: Uuid,
143        progress: ScanProgressEvent,
144    },
145    ScanCompleted {
146        scan_id: Uuid,
147        metadata: ScanEventMetadata,
148    },
149    ScanFailed {
150        scan_id: Uuid,
151        error: String,
152        metadata: ScanEventMetadata,
153    },
154}
155
156impl MediaEvent {
157    pub fn into_media(self) -> Option<Media> {
158        match self {
159            MediaEvent::MovieAdded { movie }
160            | MediaEvent::MovieUpdated { movie } => {
161                Some(Media::Movie(Box::new(movie)))
162            }
163            MediaEvent::MovieBatchFinalized { .. } => None,
164            MediaEvent::SeriesBundleFinalized { .. } => None,
165            MediaEvent::SeriesAdded { series }
166            | MediaEvent::SeriesUpdated { series } => {
167                Some(Media::Series(Box::new(series)))
168            }
169            MediaEvent::MediaDeleted { .. }
170            | MediaEvent::ScanStarted { .. }
171            | MediaEvent::ScanProgress { .. }
172            | MediaEvent::ScanCompleted { .. }
173            | MediaEvent::ScanFailed { .. } => None,
174        }
175    }
176}