diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-30 18:37:52 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-30 18:37:52 +0200 |
| commit | da0251d55d3902c90967b36b7a5441404bdf24ef (patch) | |
| tree | 6f8a74b7cddc16c35b3f9e536b9bbb86b259b545 | |
| parent | Merge pull request 'mpris merge' (#2) from mpris into main (diff) | |
| download | rsplayer-da0251d55d3902c90967b36b7a5441404bdf24ef.tar.gz rsplayer-da0251d55d3902c90967b36b7a5441404bdf24ef.zip | |
Fix synchronous HTTP calls
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/daemon.rs | 194 | ||||
| -rw-r--r-- | src/mpris.rs | 22 | ||||
| -rw-r--r-- | src/scrobbler.rs | 7 |
4 files changed, 122 insertions, 103 deletions
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "RSPlayer" | 2 | name = "RSPlayer" |
| 3 | version = "0.9.1" | 3 | version = "0.9.2" |
| 4 | edition = "2024" | 4 | edition = "2024" |
| 5 | description = "Rust music player daemon with Unix socket IPC" | 5 | description = "Rust music player daemon with Unix socket IPC" |
| 6 | license = "GPL-3.0 or later" | 6 | license = "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 | ||
| 105 | pub 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 | { | 108 | pub 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 | ||
| 140 | pub 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" | 136 | pub 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 @@ | |||
| 8 | use md5::{Digest, Md5}; | 8 | use md5::{Digest, Md5}; |
| 9 | use reqwest::blocking::Client; | 9 | use reqwest::blocking::Client; |
| 10 | use std::collections::BTreeMap; | 10 | use std::collections::BTreeMap; |
| 11 | use std::time::Duration; | ||
| 11 | 12 | ||
| 12 | pub struct Scrobbler { | 13 | pub 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 | ||
