spotify_cli/cli/commands/player/
playback.rs1use crate::endpoints::player::{
4 pause_playback, skip_to_next, skip_to_previous, start_resume_playback,
5};
6use crate::io::output::{ErrorKind, Response};
7
8use crate::cli::commands::{init_pin_store, now_playing, with_client};
9
10fn url_to_uri(input: &str) -> String {
12 if input.contains("open.spotify.com") {
13 let parts: Vec<&str> = input.split('/').collect();
14 if parts.len() >= 2 {
15 let id = parts.last().unwrap_or(&"").split('?').next().unwrap_or("");
16 let resource_type = parts.get(parts.len() - 2).unwrap_or(&"");
17 return format!("spotify:{}:{}", resource_type, id);
18 }
19 }
20 input.to_string()
21}
22
23pub async fn player_next() -> Response {
24 with_client(|client| async move {
25 match skip_to_next::skip_to_next(&client).await {
26 Ok(_) => Response::success(204, "Skipped to next track"),
27 Err(e) => Response::from_http_error(&e, "Failed to skip to next track"),
28 }
29 })
30 .await
31}
32
33pub async fn player_previous() -> Response {
34 with_client(|client| async move {
35 match skip_to_previous::skip_to_previous(&client).await {
36 Ok(_) => Response::success(204, "Skipped to previous track"),
37 Err(e) => Response::from_http_error(&e, "Failed to skip to previous track"),
38 }
39 })
40 .await
41}
42
43pub async fn player_toggle() -> Response {
44 with_client(|client| async move {
45 let playing = match now_playing::is_playing(&client).await {
46 Ok(p) => p,
47 Err(e) => return e,
48 };
49
50 if playing {
51 match pause_playback::pause_playback(&client).await {
52 Ok(_) => Response::success(204, "Playback paused"),
53 Err(e) => Response::from_http_error(&e, "Failed to pause playback"),
54 }
55 } else {
56 match start_resume_playback::start_resume_playback(&client, None, None).await {
57 Ok(_) => Response::success(204, "Playback started"),
58 Err(e) => Response::from_http_error(&e, "Failed to start playback"),
59 }
60 }
61 })
62 .await
63}
64
65pub async fn player_play(uri: Option<&str>, pin: Option<&str>) -> Response {
66 let context_uri: Option<String> = if let Some(pin_alias) = pin {
68 let store = match init_pin_store() {
69 Ok(s) => s,
70 Err(e) => return e,
71 };
72
73 match store.find_by_alias(pin_alias) {
74 Some(p) => Some(p.uri()),
75 None => return Response::err(404, "Pin not found", ErrorKind::NotFound),
76 }
77 } else {
78 uri.map(url_to_uri)
79 };
80
81 let has_uri = context_uri.is_some();
82
83 let is_track_uri = context_uri
85 .as_ref()
86 .map(|u| u.starts_with("spotify:track:"))
87 .unwrap_or(false);
88
89 with_client(|client| async move {
90 if !has_uri {
92 match now_playing::is_playing(&client).await {
93 Ok(true) => return Response::success(204, "Already playing"),
94 Ok(false) => {}
95 Err(e) => return e,
96 }
97 }
98
99 let result = if is_track_uri {
101 let track_uris = vec![context_uri.clone().unwrap()];
102 start_resume_playback::start_resume_playback(&client, None, Some(&track_uris)).await
103 } else {
104 start_resume_playback::start_resume_playback(&client, context_uri.as_deref(), None)
105 .await
106 };
107
108 match result {
109 Ok(_) => {
110 if has_uri {
111 Response::success(204, "Playing requested content")
112 } else {
113 Response::success(204, "Playback started")
114 }
115 }
116 Err(e) => Response::from_http_error(&e, "Failed to start playback"),
117 }
118 })
119 .await
120}
121
122pub async fn player_pause() -> Response {
123 with_client(|client| async move {
124 match now_playing::is_playing(&client).await {
126 Ok(false) => return Response::success(204, "Already paused"),
127 Ok(true) => {}
128 Err(e) => return e,
129 }
130
131 match pause_playback::pause_playback(&client).await {
132 Ok(_) => Response::success(204, "Playback paused"),
133 Err(e) => Response::from_http_error(&e, "Failed to pause playback"),
134 }
135 })
136 .await
137}