//! Usage analytics and watch history tracking. use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::{model::MediaId, users::UserId}; /// A tracked usage event for a media item. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UsageEvent { pub id: Uuid, pub media_id: Option, pub user_id: Option, pub event_type: UsageEventType, pub timestamp: DateTime, pub duration_secs: Option, pub context_json: Option, } /// Types of usage events that can be tracked. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum UsageEventType { View, Play, Export, Share, Search, } impl std::fmt::Display for UsageEventType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = match self { Self::View => "view", Self::Play => "play", Self::Export => "export", Self::Share => "share", Self::Search => "search", }; write!(f, "{s}") } } impl std::str::FromStr for UsageEventType { type Err = String; fn from_str(s: &str) -> std::result::Result { match s { "view" => Ok(Self::View), "play" => Ok(Self::Play), "export" => Ok(Self::Export), "share" => Ok(Self::Share), "search" => Ok(Self::Search), _ => Err(format!("unknown usage event type: {s}")), } } } /// Watch history entry tracking progress through media. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WatchHistory { pub id: Uuid, pub user_id: UserId, pub media_id: MediaId, pub progress_secs: f64, pub last_watched: DateTime, }