Skip to main content

#
jellytau_lib/jellyfin/
types.rs

    1use serde::Serialize;
    2
    3/// Configuration for Jellyfin API client
    4#[derive(Debug, Clone)]
    5pub struct JellyfinConfig {
    6    pub server_url: String,
    7    pub access_token: String,
    8    pub device_id: String,
    9}
    10
    11/// Request body for reporting playback start
    12#[derive(Debug, Serialize)]
    13#[serde(rename_all = "PascalCase")]
    14pub struct PlaybackStartRequest {
    15    pub item_id: String,
    16    pub position_ticks: i64,
    17    #[serde(skip_serializing_if = "Option::is_none")]
    18    pub play_session_id: Option<String>,
    19    pub play_command: String,
    20    pub is_paused: bool,
    21}
    22
    23/// Request body for reporting playback stopped
    24#[derive(Debug, Serialize)]
    25#[serde(rename_all = "PascalCase")]
    26pub struct PlaybackStoppedRequest {
    27    pub item_id: String,
    28    pub position_ticks: i64,
    29    #[serde(skip_serializing_if = "Option::is_none")]
    30    pub play_session_id: Option<String>,
    31}
    32
    33/// Request body for reporting playback progress
    34#[derive(Debug, Serialize)]
    35#[serde(rename_all = "PascalCase")]
    36#[allow(dead_code)] // Will be used when playback_reporting is integrated
    37pub struct PlaybackProgressRequest {
    38    pub item_id: String,
    39    pub position_ticks: i64,
    40    pub is_paused: bool,
    41    #[serde(skip_serializing_if = "Option::is_none")]
    42    pub play_session_id: Option<String>,
    43}
    44
    45#[cfg(test)]
    46mod tests {
    47    use super::*;
    48
    49    #[test]
    50    fn test_jellyfin_config_creation() {
    51        let config = JellyfinConfig {
    52            server_url: "https://jellyfin.example.com".to_string(),
    53            access_token: "token-123".to_string(),
    54            device_id: "device-456".to_string(),
    55        };
    56
    57        assert_eq!(config.server_url, "https://jellyfin.example.com");
    58        assert_eq!(config.access_token, "token-123");
    59        assert_eq!(config.device_id, "device-456");
    60    }
    61
    62    #[test]
    63    fn test_jellyfin_config_clone() {
    64        let config = JellyfinConfig {
    65            server_url: "https://server.local".to_string(),
    66            access_token: "token-abc".to_string(),
    67            device_id: "device-xyz".to_string(),
    68        };
    69
    70        let cloned = config.clone();
    71        assert_eq!(config.server_url, cloned.server_url);
    72        assert_eq!(config.access_token, cloned.access_token);
    73        assert_eq!(config.device_id, cloned.device_id);
    74    }
    75
    76    #[test]
    77    fn test_playback_start_request_serialization() {
    78        let request = PlaybackStartRequest {
    79            item_id: "item-123".to_string(),
    80            position_ticks: 0,
    81            play_session_id: Some("session-456".to_string()),
    82            play_command: "PlayNow".to_string(),
    83            is_paused: false,
    84        };
    85
    86        let json = serde_json::to_string(&request);
    87        assert!(json.is_ok());
    88        let serialized = json.unwrap();
    89        assert!(serialized.contains("item-123"));
    90        assert!(serialized.contains("PlayNow"));
    91    }
    92
    93    #[test]
    94    fn test_playback_start_request_without_session() {
    95        let request = PlaybackStartRequest {
    96            item_id: "item-789".to_string(),
    97            position_ticks: 1_000_000,
    98            play_session_id: None,
    99            play_command: "Resume".to_string(),
    100            is_paused: true,
    101        };
    102
    103        let json = serde_json::to_string(&request).unwrap();
    104        assert!(json.contains("true"));
    105        assert!(!json.contains("playSessionId"));
    106    }
    107
    108    #[test]
    109    fn test_playback_start_request_pascal_case() {
    110        let request = PlaybackStartRequest {
    111            item_id: "item-1".to_string(),
    112            position_ticks: 100,
    113            play_session_id: Some("session-1".to_string()),
    114            play_command: "Play".to_string(),
    115            is_paused: false,
    116        };
    117
    118        let json = serde_json::to_string(&request).unwrap();
    119        // Verify PascalCase serialization
    120        assert!(json.contains("ItemId"));
    121        assert!(json.contains("PositionTicks"));
    122        assert!(json.contains("PlaySessionId"));
    123        assert!(json.contains("PlayCommand"));
    124        assert!(json.contains("IsPaused"));
    125    }
    126
    127    #[test]
    128    fn test_playback_stopped_request_serialization() {
    129        let request = PlaybackStoppedRequest {
    130            item_id: "item-555".to_string(),
    131            position_ticks: 5_000_000,
    132            play_session_id: Some("session-789".to_string()),
    133        };
    134
    135        let json = serde_json::to_string(&request);
    136        assert!(json.is_ok());
    137        let serialized = json.unwrap();
    138        assert!(serialized.contains("item-555"));
    139        assert!(serialized.contains("5000000"));
    140    }
    141
    142    #[test]
    143    fn test_playback_stopped_request_without_session() {
    144        let request = PlaybackStoppedRequest {
    145            item_id: "item-000".to_string(),
    146            position_ticks: 0,
    147            play_session_id: None,
    148        };
    149
    150        let json = serde_json::to_string(&request).unwrap();
    151        assert!(json.contains("item-000"));
    152        assert!(!json.contains("playSessionId"));
    153    }
    154
    155    #[test]
    156    fn test_playback_stopped_request_pascal_case() {
    157        let request = PlaybackStoppedRequest {
    158            item_id: "i1".to_string(),
    159            position_ticks: 500,
    160            play_session_id: Some("s1".to_string()),
    161        };
    162
    163        let json = serde_json::to_string(&request).unwrap();
    164        assert!(json.contains("ItemId"));
    165        assert!(json.contains("PositionTicks"));
    166        assert!(json.contains("PlaySessionId"));
    167    }
    168
    169    #[test]
    170    fn test_playback_progress_request_serialization() {
    171        let request = PlaybackProgressRequest {
    172            item_id: "item-999".to_string(),
    173            position_ticks: 150_000_000,
    174            is_paused: false,
    175            play_session_id: Some("session-111".to_string()),
    176        };
    177
    178        let json = serde_json::to_string(&request);
    179        assert!(json.is_ok());
    180        let serialized = json.unwrap();
    181        assert!(serialized.contains("item-999"));
    182        assert!(serialized.contains("150000000"));
    183        assert!(serialized.contains("false"));
    184    }
    185
    186    #[test]
    187    fn test_playback_progress_request_paused() {
    188        let request = PlaybackProgressRequest {
    189            item_id: "item-paused".to_string(),
    190            position_ticks: 75_000_000,
    191            is_paused: true,
    192            play_session_id: None,
    193        };
    194
    195        let json = serde_json::to_string(&request).unwrap();
    196        assert!(json.contains("true"));
    197        assert!(json.contains("item-paused"));
    198    }
    199
    200    #[test]
    201    fn test_playback_progress_request_pascal_case() {
    202        let request = PlaybackProgressRequest {
    203            item_id: "i2".to_string(),
    204            position_ticks: 200,
    205            is_paused: true,
    206            play_session_id: Some("s2".to_string()),
    207        };
    208
    209        let json = serde_json::to_string(&request).unwrap();
    210        assert!(json.contains("ItemId"));
    211        assert!(json.contains("PositionTicks"));
    212        assert!(json.contains("IsPaused"));
    213        assert!(json.contains("PlaySessionId"));
    214    }
    215
    216    #[test]
    217    fn test_playback_start_request_various_position_values() {
    218        let positions = vec![0, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000];
    219
    220        for pos in positions {
    221            let request = PlaybackStartRequest {
    222                item_id: "item-test".to_string(),
    223                position_ticks: pos,
    224                play_session_id: None,
    225                play_command: "Play".to_string(),
    226                is_paused: false,
    227            };
    228
    229            let json = serde_json::to_string(&request).unwrap();
    230            assert!(json.contains("item-test"));
    231            assert!(json.contains(&pos.to_string()));
    232        }
    233    }
    234
    235    #[test]
    236    fn test_jellyfin_config_debug() {
    237        let config = JellyfinConfig {
    238            server_url: "https://debug.example.com".to_string(),
    239            access_token: "token-debug".to_string(),
    240            device_id: "device-debug".to_string(),
    241        };
    242
    243        let debug_str = format!("{:?}", config);
    244        assert!(debug_str.contains("JellyfinConfig"));
    245        assert!(debug_str.contains("https://debug.example.com"));
    246    }
    247
    248    #[test]
    249    fn test_playback_requests_creation_patterns() {
    250        // Test various creation patterns
    251        let start = PlaybackStartRequest {
    252            item_id: "i1".to_string(),
    253            position_ticks: 0,
    254            play_session_id: None,
    255            play_command: "PlayNow".to_string(),
    256            is_paused: false,
    257        };
    258
    259        let stopped = PlaybackStoppedRequest {
    260            item_id: "i1".to_string(),
    261            position_ticks: 1_000_000,
    262            play_session_id: None,
    263        };
    264
    265        let progress = PlaybackProgressRequest {
    266            item_id: "i1".to_string(),
    267            position_ticks: 500_000,
    268            is_paused: false,
    269            play_session_id: None,
    270        };
    271
    272        // All should serialize successfully
    273        assert!(serde_json::to_string(&start).is_ok());
    274        assert!(serde_json::to_string(&stopped).is_ok());
    275        assert!(serde_json::to_string(&progress).is_ok());
    276    }
    277}