spotify_cli/cli/commands/player/
settings.rs

1//! Player settings commands: volume, seek, shuffle, repeat
2
3use crate::endpoints::player::{
4    seek_to_position, set_playback_volume, set_repeat_mode, toggle_playback_shuffle,
5};
6use crate::io::output::{ErrorKind, Response};
7
8use crate::cli::commands::with_client;
9
10fn parse_position_to_ms(position: &str) -> Result<u64, String> {
11    let position = position.trim();
12
13    if position.contains(':') {
14        let parts: Vec<&str> = position.split(':').collect();
15        match parts.len() {
16            2 => {
17                let mins: u64 = parts[0]
18                    .parse()
19                    .map_err(|_| "Invalid minutes".to_string())?;
20                let secs: u64 = parts[1]
21                    .parse()
22                    .map_err(|_| "Invalid seconds".to_string())?;
23                Ok((mins * 60 + secs) * 1000)
24            }
25            3 => {
26                let hours: u64 = parts[0].parse().map_err(|_| "Invalid hours".to_string())?;
27                let mins: u64 = parts[1]
28                    .parse()
29                    .map_err(|_| "Invalid minutes".to_string())?;
30                let secs: u64 = parts[2]
31                    .parse()
32                    .map_err(|_| "Invalid seconds".to_string())?;
33                Ok((hours * 3600 + mins * 60 + secs) * 1000)
34            }
35            _ => Err("Invalid time format. Use mm:ss or hh:mm:ss".to_string()),
36        }
37    } else if let Some(ms_str) = position.strip_suffix("ms") {
38        ms_str
39            .parse()
40            .map_err(|_| "Invalid milliseconds".to_string())
41    } else if let Some(s_str) = position.strip_suffix('s') {
42        let secs: u64 = s_str.parse().map_err(|_| "Invalid seconds".to_string())?;
43        Ok(secs * 1000)
44    } else {
45        let secs: u64 = position
46            .parse()
47            .map_err(|_| "Invalid position. Use: 90, 1:30, 90s, or 5000ms".to_string())?;
48        Ok(secs * 1000)
49    }
50}
51
52pub async fn player_seek(position: &str) -> Response {
53    let position_ms = match parse_position_to_ms(position) {
54        Ok(ms) => ms,
55        Err(e) => return Response::err(400, &e, ErrorKind::Validation),
56    };
57
58    with_client(|client| async move {
59        match seek_to_position::seek_to_position(&client, position_ms).await {
60            Ok(_) => Response::success(204, "Seeked to position"),
61            Err(e) => Response::from_http_error(&e, "Failed to seek"),
62        }
63    })
64    .await
65}
66
67pub async fn player_repeat(mode: &str) -> Response {
68    let mode = mode.to_string();
69    with_client(|client| async move {
70        match set_repeat_mode::set_repeat_mode(&client, &mode).await {
71            Ok(_) => Response::success(204, format!("Repeat mode set to {}", mode)),
72            Err(e) => Response::from_http_error(&e, "Failed to set repeat mode"),
73        }
74    })
75    .await
76}
77
78pub async fn player_volume(percent: u8) -> Response {
79    with_client(|client| async move {
80        match set_playback_volume::set_playback_volume(&client, percent).await {
81            Ok(_) => Response::success(204, format!("Volume set to {}%", percent)),
82            Err(e) => Response::from_http_error(&e, "Failed to set volume"),
83        }
84    })
85    .await
86}
87
88pub async fn player_shuffle(state: &str) -> Response {
89    let enabled = state == "on";
90    with_client(|client| async move {
91        match toggle_playback_shuffle::toggle_playback_shuffle(&client, enabled).await {
92            Ok(_) => Response::success(
93                204,
94                if enabled {
95                    "Shuffle enabled"
96                } else {
97                    "Shuffle disabled"
98                },
99            ),
100            Err(e) => Response::from_http_error(&e, "Failed to set shuffle"),
101        }
102    })
103    .await
104}