aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-07-30 18:37:52 +0200
committersalmonik <salmonik@tuta.io>2026-07-30 18:37:52 +0200
commitda0251d55d3902c90967b36b7a5441404bdf24ef (patch)
tree6f8a74b7cddc16c35b3f9e536b9bbb86b259b545
parentMerge pull request 'mpris merge' (#2) from mpris into main (diff)
downloadrsplayer-da0251d55d3902c90967b36b7a5441404bdf24ef.tar.gz
rsplayer-da0251d55d3902c90967b36b7a5441404bdf24ef.zip
Fix synchronous HTTP calls
-rw-r--r--Cargo.toml2
-rw-r--r--src/daemon.rs194
-rw-r--r--src/mpris.rs22
-rw-r--r--src/scrobbler.rs7
4 files changed, 122 insertions, 103 deletions
diff --git a/Cargo.toml b/Cargo.toml
index ce8ca48..42e0442 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "RSPlayer" 2name = "RSPlayer"
3version = "0.9.1" 3version = "0.9.2"
4edition = "2024" 4edition = "2024"
5description = "Rust music player daemon with Unix socket IPC" 5description = "Rust music player daemon with Unix socket IPC"
6license = "GPL-3.0 or later" 6license = "GPL-3.0 or later"
diff --git a/src/daemon.rs b/src/daemon.rs
index 1c918a8..979c6b4 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -102,57 +102,86 @@ impl ScrobbleState {
102 } 102 }
103} 103}
104 104
105pub fn try_scrobble_current(scrobbler: &RwLock<Scrobbler>, state: &mut ScrobbleState) { 105/// Fire-and-forget version of `send_now_playing`.
106 if let Some(ref track) = state.track 106/// Spawns the scrobble HTTP call on a background thread so the caller
107 && track.is_stream 107/// (e.g. a Unix socket handler) is never blocked by network latency.
108 { 108pub fn spawn_send_now_playing(scrobbler: &Arc<RwLock<Scrobbler>>, track: &Track) {
109 crate::debug_eprintln(format_args!("scrobble: skipping scrobble for stream track")); 109 if track.is_stream {
110 state.clear();
111 return; 110 return;
112 } 111 }
113 if state.should_scrobble() 112 let s = Arc::clone(scrobbler);
114 && let (Some(track), Some(ts)) = (&state.track, state.timestamp) 113 let artist = track
115 { 114 .artist
116 let artist = track.artist.as_deref().unwrap_or("(unknown artist)"); 115 .clone()
117 let title = track.title.as_deref().unwrap_or("(unknown title)"); 116 .unwrap_or_else(|| "(unknown artist)".into());
118 let album = track.album.as_deref(); 117 let title = track
118 .title
119 .clone()
120 .unwrap_or_else(|| "(unknown title)".into());
121 let album = track.album.clone();
122 thread::spawn(move || {
119 crate::debug_eprintln(format_args!( 123 crate::debug_eprintln(format_args!(
120 "scrobble: sending scrobble for '{} - {}' at ts={}", 124 "scrobble: sending now_playing for '{} - {}'",
121 artist, title, ts 125 artist, title
122 )); 126 ));
123 if let Ok(s) = scrobbler.read() { 127 if let Ok(s) = s.read() {
124 s.scrobble(artist, title, album, ts); 128 s.now_playing(&artist, &title, album.as_deref());
125 } 129 }
126 } else { 130 });
127 let title = state
128 .track
129 .as_ref()
130 .and_then(|t| t.title.as_deref())
131 .unwrap_or("(no track)");
132 crate::debug_eprintln(format_args!(
133 "scrobble: skipping scrobble for '{}' — not enough play time",
134 title
135 ));
136 }
137 state.clear();
138} 131}
139 132
140pub fn send_now_playing(scrobbler: &RwLock<Scrobbler>, track: &Track) { 133/// Fire-and-forget version of `try_scrobble_current`.
141 if track.is_stream { 134/// Extracts the scrobble decision and track data under the state lock,
142 crate::debug_eprintln(format_args!( 135/// then spawns the HTTP call on a background thread.
143 "scrobble: skipping now_playing for stream track" 136pub fn spawn_try_scrobble_current(
144 )); 137 scrobbler: &Arc<RwLock<Scrobbler>>,
145 return; 138 state: &Arc<Mutex<ScrobbleState>>,
146 } 139) {
147 let artist = track.artist.as_deref().unwrap_or("(unknown artist)"); 140 let snapshot = {
148 let title = track.title.as_deref().unwrap_or("(unknown title)"); 141 let mut s = match state.lock() {
149 let album = track.album.as_deref(); 142 Ok(g) => g,
150 crate::debug_eprintln(format_args!( 143 Err(_) => return,
151 "scrobble: sending now_playing for '{} - {}'", 144 };
152 artist, title 145 if let Some(ref track) = s.track
153 )); 146 && track.is_stream
154 if let Ok(s) = scrobbler.read() { 147 {
155 s.now_playing(artist, title, album); 148 crate::debug_eprintln(format_args!("scrobble: skipping scrobble for stream track"));
149 s.clear();
150 return;
151 }
152 if s.should_scrobble() {
153 let track = s.track.take();
154 let ts = s.timestamp.take();
155 s.start_time = None;
156 (track, ts)
157 } else {
158 let title = s
159 .track
160 .as_ref()
161 .and_then(|t| t.title.as_deref())
162 .unwrap_or("(no track)");
163 crate::debug_eprintln(format_args!(
164 "scrobble: skipping scrobble for '{}' — not enough play time",
165 title
166 ));
167 s.clear();
168 (None, None)
169 }
170 };
171 if let (Some(track), Some(ts)) = snapshot {
172 let s = Arc::clone(scrobbler);
173 thread::spawn(move || {
174 let artist = track.artist.as_deref().unwrap_or("(unknown artist)");
175 let title = track.title.as_deref().unwrap_or("(unknown title)");
176 let album = track.album.as_deref();
177 crate::debug_eprintln(format_args!(
178 "scrobble: sending scrobble for '{} - {}' at ts={}",
179 artist, title, ts
180 ));
181 if let Ok(s) = s.read() {
182 s.scrobble(artist, title, album, ts);
183 }
184 });
156 } 185 }
157} 186}
158 187
@@ -246,6 +275,15 @@ pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
246 // Save full config 275 // Save full config
247 config.save()?; 276 config.save()?;
248 277
278 // Warm up D-Bus session connection so the first notification
279 // doesn't pay the full connection-establishment cost.
280 if let Err(e) = zbus::blocking::Connection::session() {
281 crate::debug_eprintln(format_args!(
282 "daemon: failed to connect to D-Bus session bus: {}",
283 e
284 ));
285 }
286
249 // Start MPRIS D-Bus server for external control (playerctl, etc.) 287 // Start MPRIS D-Bus server for external control (playerctl, etc.)
250 crate::mpris::start_mpris_server( 288 crate::mpris::start_mpris_server(
251 Arc::clone(&audio_engine), 289 Arc::clone(&audio_engine),
@@ -282,10 +320,8 @@ pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
282 if audio_engine.poll_track_ended() 320 if audio_engine.poll_track_ended()
283 && let Ok(mut q) = queue.lock() 321 && let Ok(mut q) = queue.lock()
284 { 322 {
285 // Scrobble the track that just ended 323 // Scrobble the track that just ended (fire-and-forget)
286 if let Ok(mut s) = scrobble_state.lock() { 324 spawn_try_scrobble_current(&scrobbler, &scrobble_state);
287 try_scrobble_current(&scrobbler, &mut s);
288 }
289 let original_position = q.position(); 325 let original_position = q.position();
290 let mut started = false; 326 let mut started = false;
291 let mut had_next = false; 327 let mut had_next = false;
@@ -297,8 +333,8 @@ pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
297 if wait_for_start(&audio_engine, &next_track, 500) { 333 if wait_for_start(&audio_engine, &next_track, 500) {
298 started = true; 334 started = true;
299 let track = Track::from_path(next_track.clone()); 335 let track = Track::from_path(next_track.clone());
300 // Send now playing and record scrobble start 336 // Send now playing and record scrobble start (fire-and-forget)
301 send_now_playing(&scrobbler, &track); 337 spawn_send_now_playing(&scrobbler, &track);
302 if let Ok(mut s) = scrobble_state.lock() { 338 if let Ok(mut s) = scrobble_state.lock() {
303 s.start(track.clone()); 339 s.start(track.clone());
304 } 340 }
@@ -578,7 +614,7 @@ fn process_command(
578 audio: &AudioEngine, 614 audio: &AudioEngine,
579 shutdown_flag: &Arc<AtomicBool>, 615 shutdown_flag: &Arc<AtomicBool>,
580 queue: &Arc<Mutex<Queue>>, 616 queue: &Arc<Mutex<Queue>>,
581 scrobbler: &RwLock<Scrobbler>, 617 scrobbler: &Arc<RwLock<Scrobbler>>,
582 scrobble_state: &Arc<Mutex<ScrobbleState>>, 618 scrobble_state: &Arc<Mutex<ScrobbleState>>,
583) -> String { 619) -> String {
584 match cmd { 620 match cmd {
@@ -621,9 +657,7 @@ fn process_command(
621 } 657 }
622 } 658 }
623 "stop" => { 659 "stop" => {
624 if let Ok(mut s) = scrobble_state.lock() { 660 spawn_try_scrobble_current(scrobbler, scrobble_state);
625 try_scrobble_current(scrobbler, &mut s);
626 }
627 audio.stop(); 661 audio.stop();
628 if let Ok(mut q) = queue.lock() { 662 if let Ok(mut q) = queue.lock() {
629 q.clear(); 663 q.clear();
@@ -670,14 +704,12 @@ fn process_command(
670 } 704 }
671 } 705 }
672 "next" => { 706 "next" => {
673 if let Ok(mut s) = scrobble_state.lock() { 707 spawn_try_scrobble_current(scrobbler, scrobble_state);
674 try_scrobble_current(scrobbler, &mut s);
675 }
676 if let Ok(mut q) = queue.lock() { 708 if let Ok(mut q) = queue.lock() {
677 if let Some(next_track) = q.next() { 709 if let Some(next_track) = q.next() {
678 audio.play(next_track.clone()); 710 audio.play(next_track.clone());
679 let track = Track::from_path(next_track.clone()); 711 let track = Track::from_path(next_track.clone());
680 send_now_playing(scrobbler, &track); 712 spawn_send_now_playing(scrobbler, &track);
681 if let Ok(mut s) = scrobble_state.lock() { 713 if let Ok(mut s) = scrobble_state.lock() {
682 s.start(track.clone()); 714 s.start(track.clone());
683 } 715 }
@@ -695,14 +727,12 @@ fn process_command(
695 } 727 }
696 } 728 }
697 "previous" => { 729 "previous" => {
698 if let Ok(mut s) = scrobble_state.lock() { 730 spawn_try_scrobble_current(scrobbler, scrobble_state);
699 try_scrobble_current(scrobbler, &mut s);
700 }
701 if let Ok(mut q) = queue.lock() { 731 if let Ok(mut q) = queue.lock() {
702 if let Some(prev_track) = q.previous() { 732 if let Some(prev_track) = q.previous() {
703 audio.play(prev_track.clone()); 733 audio.play(prev_track.clone());
704 let track = Track::from_path(prev_track.clone()); 734 let track = Track::from_path(prev_track.clone());
705 send_now_playing(scrobbler, &track); 735 spawn_send_now_playing(scrobbler, &track);
706 if let Ok(mut s) = scrobble_state.lock() { 736 if let Ok(mut s) = scrobble_state.lock() {
707 s.start(track.clone()); 737 s.start(track.clone());
708 } 738 }
@@ -878,9 +908,7 @@ fn process_command(
878 let was_empty = q.is_empty(); 908 let was_empty = q.is_empty();
879 match position { 909 match position {
880 "now" => { 910 "now" => {
881 if let Ok(mut s) = scrobble_state.lock() { 911 spawn_try_scrobble_current(scrobbler, scrobble_state);
882 try_scrobble_current(scrobbler, &mut s);
883 }
884 // album_tracks[0] is the track to start at; insert the 912 // album_tracks[0] is the track to start at; insert the
885 // whole block in correct order. 913 // whole block in correct order.
886 if was_empty { 914 if was_empty {
@@ -896,7 +924,7 @@ fn process_command(
896 } 924 }
897 if let Some(track) = q.track_at(q.position()) { 925 if let Some(track) = q.track_at(q.position()) {
898 audio.play(track.path.clone()); 926 audio.play(track.path.clone());
899 send_now_playing(scrobbler, track); 927 spawn_send_now_playing(scrobbler, track);
900 if let Ok(mut s) = scrobble_state.lock() { 928 if let Ok(mut s) = scrobble_state.lock() {
901 s.start(track.clone()); 929 s.start(track.clone());
902 } 930 }
@@ -934,7 +962,7 @@ fn process_command(
934 && let Some(track) = q.current() 962 && let Some(track) = q.current()
935 { 963 {
936 audio.play(track.path.clone()); 964 audio.play(track.path.clone());
937 send_now_playing(scrobbler, track); 965 spawn_send_now_playing(scrobbler, track);
938 if let Ok(mut s) = scrobble_state.lock() { 966 if let Ok(mut s) = scrobble_state.lock() {
939 s.start(track.clone()); 967 s.start(track.clone());
940 } 968 }
@@ -999,9 +1027,7 @@ fn process_command(
999 let track = Track::from_url(&path_str); 1027 let track = Track::from_url(&path_str);
1000 match position { 1028 match position {
1001 "now" => { 1029 "now" => {
1002 if let Ok(mut s) = scrobble_state.lock() { 1030 spawn_try_scrobble_current(scrobbler, scrobble_state);
1003 try_scrobble_current(scrobbler, &mut s);
1004 }
1005 q.push_track(track.clone()); 1031 q.push_track(track.clone());
1006 if !was_empty { 1032 if !was_empty {
1007 let new_pos = q.position() + 1; 1033 let new_pos = q.position() + 1;
@@ -1009,7 +1035,7 @@ fn process_command(
1009 } 1035 }
1010 if let Some(track) = q.track_at(q.position()) { 1036 if let Some(track) = q.track_at(q.position()) {
1011 audio.play(track.path.clone()); 1037 audio.play(track.path.clone());
1012 send_now_playing(scrobbler, track); 1038 spawn_send_now_playing(scrobbler, track);
1013 if let Ok(mut s) = scrobble_state.lock() { 1039 if let Ok(mut s) = scrobble_state.lock() {
1014 s.start(track.clone()); 1040 s.start(track.clone());
1015 } 1041 }
@@ -1031,7 +1057,7 @@ fn process_command(
1031 && let Some(track) = q.current() 1057 && let Some(track) = q.current()
1032 { 1058 {
1033 audio.play(track.path.clone()); 1059 audio.play(track.path.clone());
1034 send_now_playing(scrobbler, track); 1060 spawn_send_now_playing(scrobbler, track);
1035 if let Ok(mut s) = scrobble_state.lock() { 1061 if let Ok(mut s) = scrobble_state.lock() {
1036 s.start(track.clone()); 1062 s.start(track.clone());
1037 } 1063 }
@@ -1052,9 +1078,7 @@ fn process_command(
1052 let was_empty = q.is_empty(); 1078 let was_empty = q.is_empty();
1053 match position { 1079 match position {
1054 "now" => { 1080 "now" => {
1055 if let Ok(mut s) = scrobble_state.lock() { 1081 spawn_try_scrobble_current(scrobbler, scrobble_state);
1056 try_scrobble_current(scrobbler, &mut s);
1057 }
1058 q.insert_after_current(path.clone()); 1082 q.insert_after_current(path.clone());
1059 if !was_empty { 1083 if !was_empty {
1060 let new_pos = q.position() + 1; 1084 let new_pos = q.position() + 1;
@@ -1062,7 +1086,7 @@ fn process_command(
1062 } 1086 }
1063 if let Some(track) = q.track_at(q.position()) { 1087 if let Some(track) = q.track_at(q.position()) {
1064 audio.play(track.path.clone()); 1088 audio.play(track.path.clone());
1065 send_now_playing(scrobbler, track); 1089 spawn_send_now_playing(scrobbler, track);
1066 if let Ok(mut s) = scrobble_state.lock() { 1090 if let Ok(mut s) = scrobble_state.lock() {
1067 s.start(track.clone()); 1091 s.start(track.clone());
1068 } 1092 }
@@ -1083,7 +1107,7 @@ fn process_command(
1083 if was_empty && !was_playing { 1107 if was_empty && !was_playing {
1084 let track = Track::from_path(path.clone()); 1108 let track = Track::from_path(path.clone());
1085 audio.play(path); 1109 audio.play(path);
1086 send_now_playing(scrobbler, &track); 1110 spawn_send_now_playing(scrobbler, &track);
1087 if let Ok(mut s) = scrobble_state.lock() { 1111 if let Ok(mut s) = scrobble_state.lock() {
1088 s.start(track.clone()); 1112 s.start(track.clone());
1089 } 1113 }
@@ -1106,9 +1130,7 @@ fn process_command(
1106 let was_empty = q.is_empty(); 1130 let was_empty = q.is_empty();
1107 match position { 1131 match position {
1108 "now" => { 1132 "now" => {
1109 if let Ok(mut s) = scrobble_state.lock() { 1133 spawn_try_scrobble_current(scrobbler, scrobble_state);
1110 try_scrobble_current(scrobbler, &mut s);
1111 }
1112 for file in files.iter().rev() { 1134 for file in files.iter().rev() {
1113 q.insert_after_current(file.clone()); 1135 q.insert_after_current(file.clone());
1114 } 1136 }
@@ -1118,7 +1140,7 @@ fn process_command(
1118 } 1140 }
1119 if let Some(track) = q.track_at(q.position()) { 1141 if let Some(track) = q.track_at(q.position()) {
1120 audio.play(track.path.clone()); 1142 audio.play(track.path.clone());
1121 send_now_playing(scrobbler, track); 1143 spawn_send_now_playing(scrobbler, track);
1122 if let Ok(mut s) = scrobble_state.lock() { 1144 if let Ok(mut s) = scrobble_state.lock() {
1123 s.start(track.clone()); 1145 s.start(track.clone());
1124 } 1146 }
@@ -1152,7 +1174,7 @@ fn process_command(
1152 q.shuffle_initial(); 1174 q.shuffle_initial();
1153 if let Some(track) = q.current() { 1175 if let Some(track) = q.current() {
1154 audio.play(track.path.clone()); 1176 audio.play(track.path.clone());
1155 send_now_playing(scrobbler, track); 1177 spawn_send_now_playing(scrobbler, track);
1156 if let Ok(mut s) = scrobble_state.lock() { 1178 if let Ok(mut s) = scrobble_state.lock() {
1157 s.start(track.clone()); 1179 s.start(track.clone());
1158 } 1180 }
@@ -1206,9 +1228,7 @@ fn process_command(
1206 let was_empty = q.is_empty(); 1228 let was_empty = q.is_empty();
1207 match position { 1229 match position {
1208 "now" => { 1230 "now" => {
1209 if let Ok(mut s) = scrobble_state.lock() { 1231 spawn_try_scrobble_current(scrobbler, scrobble_state);
1210 try_scrobble_current(scrobbler, &mut s);
1211 }
1212 for t in tracks.iter().rev() { 1232 for t in tracks.iter().rev() {
1213 q.insert_after_current(t.path.clone()); 1233 q.insert_after_current(t.path.clone());
1214 } 1234 }
@@ -1218,7 +1238,7 @@ fn process_command(
1218 } 1238 }
1219 if let Some(track) = q.track_at(q.position()) { 1239 if let Some(track) = q.track_at(q.position()) {
1220 audio.play(track.path.clone()); 1240 audio.play(track.path.clone());
1221 send_now_playing(scrobbler, track); 1241 spawn_send_now_playing(scrobbler, track);
1222 if let Ok(mut s) = scrobble_state.lock() { 1242 if let Ok(mut s) = scrobble_state.lock() {
1223 s.start(track.clone()); 1243 s.start(track.clone());
1224 } 1244 }
@@ -1262,7 +1282,7 @@ fn process_command(
1262 } 1282 }
1263 if let Some(track) = q.current() { 1283 if let Some(track) = q.current() {
1264 audio.play(track.path.clone()); 1284 audio.play(track.path.clone());
1265 send_now_playing(scrobbler, track); 1285 spawn_send_now_playing(scrobbler, track);
1266 if let Ok(mut s) = scrobble_state.lock() { 1286 if let Ok(mut s) = scrobble_state.lock() {
1267 s.start(track.clone()); 1287 s.start(track.clone());
1268 } 1288 }
diff --git a/src/mpris.rs b/src/mpris.rs
index a7ac360..39630b0 100644
--- a/src/mpris.rs
+++ b/src/mpris.rs
@@ -162,14 +162,12 @@ impl MprisPlayer {
162 162
163 fn next(&self) { 163 fn next(&self) {
164 crate::debug_eprintln(format_args!("mpris: next")); 164 crate::debug_eprintln(format_args!("mpris: next"));
165 if let Ok(mut s) = self.scrobble_state.lock() { 165 crate::daemon::spawn_try_scrobble_current(&self.scrobbler, &self.scrobble_state);
166 crate::daemon::try_scrobble_current(&self.scrobbler, &mut s);
167 }
168 if let Ok(mut q) = self.queue.lock() { 166 if let Ok(mut q) = self.queue.lock() {
169 if let Some(next_path) = q.next() { 167 if let Some(next_path) = q.next() {
170 self.audio.play(next_path.clone()); 168 self.audio.play(next_path.clone());
171 let track = Track::from_path(next_path); 169 let track = Track::from_path(next_path);
172 crate::daemon::send_now_playing(&self.scrobbler, &track); 170 crate::daemon::spawn_send_now_playing(&self.scrobbler, &track);
173 if let Ok(mut s) = self.scrobble_state.lock() { 171 if let Ok(mut s) = self.scrobble_state.lock() {
174 s.start(track.clone()); 172 s.start(track.clone());
175 } 173 }
@@ -185,15 +183,13 @@ impl MprisPlayer {
185 183
186 fn previous(&self) { 184 fn previous(&self) {
187 crate::debug_eprintln(format_args!("mpris: previous")); 185 crate::debug_eprintln(format_args!("mpris: previous"));
188 if let Ok(mut s) = self.scrobble_state.lock() { 186 crate::daemon::spawn_try_scrobble_current(&self.scrobbler, &self.scrobble_state);
189 crate::daemon::try_scrobble_current(&self.scrobbler, &mut s);
190 }
191 if let Ok(mut q) = self.queue.lock() 187 if let Ok(mut q) = self.queue.lock()
192 && let Some(prev_path) = q.previous() 188 && let Some(prev_path) = q.previous()
193 { 189 {
194 self.audio.play(prev_path.clone()); 190 self.audio.play(prev_path.clone());
195 let track = Track::from_path(prev_path); 191 let track = Track::from_path(prev_path);
196 crate::daemon::send_now_playing(&self.scrobbler, &track); 192 crate::daemon::spawn_send_now_playing(&self.scrobbler, &track);
197 if let Ok(mut s) = self.scrobble_state.lock() { 193 if let Ok(mut s) = self.scrobble_state.lock() {
198 s.start(track.clone()); 194 s.start(track.clone());
199 } 195 }
@@ -221,9 +217,7 @@ impl MprisPlayer {
221 217
222 fn stop(&self) { 218 fn stop(&self) {
223 crate::debug_eprintln(format_args!("mpris: stop")); 219 crate::debug_eprintln(format_args!("mpris: stop"));
224 if let Ok(mut s) = self.scrobble_state.lock() { 220 crate::daemon::spawn_try_scrobble_current(&self.scrobbler, &self.scrobble_state);
225 crate::daemon::try_scrobble_current(&self.scrobbler, &mut s);
226 }
227 self.audio.stop(); 221 self.audio.stop();
228 if let Ok(mut q) = self.queue.lock() { 222 if let Ok(mut q) = self.queue.lock() {
229 q.clear(); 223 q.clear();
@@ -240,7 +234,7 @@ impl MprisPlayer {
240 && let Some(track) = q.current() 234 && let Some(track) = q.current()
241 { 235 {
242 self.audio.play(track.path.clone()); 236 self.audio.play(track.path.clone());
243 crate::daemon::send_now_playing(&self.scrobbler, track); 237 crate::daemon::spawn_send_now_playing(&self.scrobbler, track);
244 if let Ok(mut s) = self.scrobble_state.lock() { 238 if let Ok(mut s) = self.scrobble_state.lock() {
245 s.start(track.clone()); 239 s.start(track.clone());
246 } 240 }
@@ -277,7 +271,7 @@ impl MprisPlayer {
277 && let Some(t) = q.current() 271 && let Some(t) = q.current()
278 { 272 {
279 self.audio.play(t.path.clone()); 273 self.audio.play(t.path.clone());
280 crate::daemon::send_now_playing(&self.scrobbler, t); 274 crate::daemon::spawn_send_now_playing(&self.scrobbler, t);
281 if let Ok(mut s) = self.scrobble_state.lock() { 275 if let Ok(mut s) = self.scrobble_state.lock() {
282 s.start(t.clone()); 276 s.start(t.clone());
283 } 277 }
@@ -289,7 +283,7 @@ impl MprisPlayer {
289 if was_empty && !was_playing { 283 if was_empty && !was_playing {
290 let track = Track::from_path(path); 284 let track = Track::from_path(path);
291 self.audio.play(track.path.clone()); 285 self.audio.play(track.path.clone());
292 crate::daemon::send_now_playing(&self.scrobbler, &track); 286 crate::daemon::spawn_send_now_playing(&self.scrobbler, &track);
293 if let Ok(mut s) = self.scrobble_state.lock() { 287 if let Ok(mut s) = self.scrobble_state.lock() {
294 s.start(track.clone()); 288 s.start(track.clone());
295 } 289 }
diff --git a/src/scrobbler.rs b/src/scrobbler.rs
index 40d3ce6..e6e8a73 100644
--- a/src/scrobbler.rs
+++ b/src/scrobbler.rs
@@ -8,6 +8,7 @@
8use md5::{Digest, Md5}; 8use md5::{Digest, Md5};
9use reqwest::blocking::Client; 9use reqwest::blocking::Client;
10use std::collections::BTreeMap; 10use std::collections::BTreeMap;
11use std::time::Duration;
11 12
12pub struct Scrobbler { 13pub struct Scrobbler {
13 pub lastfm_api_key: Option<String>, 14 pub lastfm_api_key: Option<String>,
@@ -33,7 +34,11 @@ impl Scrobbler {
33 lastfm_api_secret, 34 lastfm_api_secret,
34 lastfm_session_key, 35 lastfm_session_key,
35 listenbrainz_token, 36 listenbrainz_token,
36 client: Client::new(), 37 client: Client::builder()
38 .connect_timeout(Duration::from_secs(10))
39 .timeout(Duration::from_secs(30))
40 .build()
41 .expect("valid reqwest Client"),
37 } 42 }
38 } 43 }
39 44