Skip to content

Commit fcb5394

Browse files
authored
fix: move in playlists sometimes failing (#184)
1 parent fc2552c commit fcb5394

File tree

9 files changed

+126
-152
lines changed

9 files changed

+126
-152
lines changed

src/context.rs

+29-3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ use crate::{
1111
events::ClientRequest,
1212
lrc::{Lrc, LrcIndex},
1313
macros::status_warn,
14+
mpd_query::MpdQuerySync,
1415
},
1516
AppEvent, MpdCommand, MpdQuery, MpdQueryResult, WorkRequest,
1617
};
1718
use anyhow::{bail, Result};
1819
use bon::bon;
19-
use crossbeam::channel::{SendError, Sender};
20+
use crossbeam::channel::{bounded, SendError, Sender};
2021

2122
pub struct AppContext {
2223
pub config: &'static Config,
@@ -78,6 +79,31 @@ impl AppContext {
7879
self.needs_render.replace(false);
7980
}
8081

82+
pub fn query_sync<T: Send + Sync + 'static>(
83+
&self,
84+
on_done: impl FnOnce(&mut Client<'_>) -> Result<T> + Send + 'static,
85+
) -> Result<T> {
86+
let (tx, rx) = bounded(1);
87+
let query = MpdQuerySync {
88+
callback: Box::new(|client| Ok(MpdQueryResult::Any(Box::new((on_done)(client)?)))),
89+
tx,
90+
};
91+
92+
if let Err(err) = self.client_request_sender.send(ClientRequest::QuerySync(query)) {
93+
log::error!(error:? = err; "Failed to send query request");
94+
bail!("Failed to send sync query request");
95+
}
96+
97+
if let MpdQueryResult::Any(any) = rx.recv()? {
98+
if let Ok(val) = any.downcast::<T>() {
99+
return Ok(*val);
100+
}
101+
bail!("Received unknown type answer for sync query request",);
102+
}
103+
104+
bail!("Received unknown MpdQueryResult for sync query request");
105+
}
106+
81107
#[builder(finish_fn(name = query))]
82108
pub fn query(
83109
&self,
@@ -92,13 +118,13 @@ impl AppContext {
92118
replace_id,
93119
callback: Box::new(on_done),
94120
};
95-
if let Err(err) = self.client_request_sender.send(ClientRequest::MpdQuery(query)) {
121+
if let Err(err) = self.client_request_sender.send(ClientRequest::Query(query)) {
96122
log::error!(error:? = err; "Failed to send query request");
97123
}
98124
}
99125

100126
pub fn command(&self, callback: impl FnOnce(&mut Client<'_>) -> Result<()> + Send + 'static) {
101-
if let Err(err) = self.client_request_sender.send(ClientRequest::MpdCommand(MpdCommand {
127+
if let Err(err) = self.client_request_sender.send(ClientRequest::Command(MpdCommand {
102128
callback: Box::new(callback),
103129
})) {
104130
log::error!(error:? = err; "Failed to send command request");

src/core/client.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ fn client_task(client_rx: &Receiver<ClientRequest>, event_tx: &Sender<AppEvent>,
141141
buffer.push_back(request);
142142
}
143143
if buffer.iter().any(|request2| {
144-
if let (ClientRequest::MpdQuery(q1), ClientRequest::MpdQuery(q2)) =
144+
if let (ClientRequest::Query(q1), ClientRequest::Query(q2)) =
145145
(&request, &request2)
146146
{
147147
q1.should_be_skipped(q2)
@@ -283,14 +283,19 @@ fn check_connection(
283283

284284
fn handle_client_request(client: &mut Client<'_>, request: ClientRequest) -> Result<WorkDone> {
285285
match request {
286-
ClientRequest::MpdQuery(query) => Ok(WorkDone::MpdCommandFinished {
286+
ClientRequest::Query(query) => Ok(WorkDone::MpdCommandFinished {
287287
id: query.id,
288288
target: query.target,
289289
data: (query.callback)(client)?,
290290
}),
291-
ClientRequest::MpdCommand(command) => {
291+
ClientRequest::Command(command) => {
292292
(command.callback)(client)?;
293293
Ok(WorkDone::None)
294294
}
295+
ClientRequest::QuerySync(query) => {
296+
let result = (query.callback)(client)?;
297+
query.tx.send(result)?;
298+
Ok(WorkDone::None)
299+
}
295300
}
296301
}

src/core/update_loop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl UpdateLoop {
4848
}
4949

5050
std::thread::sleep(update_interval);
51-
if let Err(err) = work_tx.send(ClientRequest::MpdQuery(MpdQuery {
51+
if let Err(err) = work_tx.send(ClientRequest::Query(MpdQuery {
5252
id: "global_status_update",
5353
target: None,
5454
replace_id: None,

src/core/work.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ fn handle_work_request(
3939
WorkRequest::Command(command) => {
4040
let callback = command.execute(config)?; // TODO log
4141
try_skip!(
42-
client_tx.send(ClientRequest::MpdCommand(MpdCommand { callback })),
42+
client_tx.send(ClientRequest::Command(MpdCommand { callback })),
4343
"Failed to send client request to complete command"
4444
);
4545
Ok(WorkDone::None)

src/shared/events.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ use crossterm::event::KeyEvent;
99
use super::{
1010
lrc::LrcIndex,
1111
mouse_event::MouseEvent,
12-
mpd_query::{MpdCommand, MpdQuery, MpdQueryResult},
12+
mpd_query::{MpdCommand, MpdQuery, MpdQueryResult, MpdQuerySync},
1313
};
1414

1515
#[derive(Debug)]
1616
#[allow(unused)]
1717
pub(crate) enum ClientRequest {
18-
MpdQuery(MpdQuery),
19-
MpdCommand(MpdCommand),
18+
Query(MpdQuery),
19+
QuerySync(MpdQuerySync),
20+
Command(MpdCommand),
2021
}
2122

2223
#[derive(Debug)]

src/shared/mpd_query.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::any::Any;
2+
13
use crate::{
24
config::tabs::PaneType,
35
mpd::{
@@ -8,6 +10,7 @@ use crate::{
810
};
911
use anyhow::Result;
1012
use bon::Builder;
13+
use crossbeam::channel::Sender;
1114
use ratatui::widgets::ListItem;
1215

1316
#[derive(derive_more::Debug, Builder)]
@@ -19,6 +22,13 @@ pub(crate) struct MpdQuery {
1922
pub callback: Box<dyn FnOnce(&mut Client<'_>) -> Result<MpdQueryResult> + Send>,
2023
}
2124

25+
#[derive(derive_more::Debug, Builder)]
26+
pub(crate) struct MpdQuerySync {
27+
#[debug(skip)]
28+
pub callback: Box<dyn FnOnce(&mut Client<'_>) -> Result<MpdQueryResult> + Send>,
29+
pub tx: Sender<MpdQueryResult>,
30+
}
31+
2232
#[derive(derive_more::Debug)]
2333
pub struct MpdCommand {
2434
#[debug(skip)]
@@ -68,4 +78,5 @@ pub(crate) enum MpdQueryResult {
6878
Outputs(Vec<Output>),
6979
Decoders(Vec<Decoder>),
7080
ExternalCommand(&'static [&'static str], Vec<Song>),
81+
Any(Box<dyn Any + Send + Sync>),
7182
}

src/ui/panes/album_art.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ mod tests {
206206
if should_search {
207207
assert!(matches!(
208208
rx.recv_timeout(Duration::from_millis(100)).unwrap(),
209-
ClientRequest::MpdQuery(MpdQuery {
209+
ClientRequest::Query(MpdQuery {
210210
id: ALBUM_ART,
211211
replace_id: Some(ALBUM_ART),
212212
target: Some(PaneType::AlbumArt),
@@ -253,7 +253,7 @@ mod tests {
253253
if should_search {
254254
assert!(matches!(
255255
rx.recv_timeout(Duration::from_millis(100)).unwrap(),
256-
ClientRequest::MpdQuery(MpdQuery {
256+
ClientRequest::Query(MpdQuery {
257257
id: ALBUM_ART,
258258
replace_id: Some(ALBUM_ART),
259259
target: Some(PaneType::AlbumArt),

src/ui/panes/playlists.rs

+24-25
Original file line numberDiff line numberDiff line change
@@ -217,30 +217,6 @@ impl Pane for PlaylistsPane {
217217
self.stack = DirStack::new(data);
218218
self.prepare_preview(context)?;
219219
}
220-
(REINIT, MpdQueryResult::SongsList { data: songs, .. }) => {
221-
// Select the same song by filename or index as before
222-
let old_viewport_len = self.stack.current().state.viewport_len();
223-
224-
self.stack_mut()
225-
.replace(songs.into_iter().map(DirOrSong::Song).collect());
226-
self.prepare_preview(context)?;
227-
if let Some((idx, song)) = &self.selected_song {
228-
let idx_to_select = self
229-
.stack
230-
.current()
231-
.items
232-
.iter()
233-
.find_position(|item| item.as_path() == song)
234-
.map_or(*idx, |(idx, _)| idx);
235-
self.stack.current_mut().state.set_viewport_len(old_viewport_len);
236-
self.stack
237-
.current_mut()
238-
.state
239-
.select(Some(idx_to_select), context.config.scrolloff);
240-
};
241-
self.prepare_preview(context)?;
242-
context.render()?;
243-
}
244220
(REINIT, MpdQueryResult::DirOrSong { data, .. }) => {
245221
let mut new_stack = DirStack::new(data);
246222
let old_viewport_len = self.stack.current().state.viewport_len();
@@ -268,10 +244,33 @@ impl Pane for PlaylistsPane {
268244
if let Some((idx, DirOrSong::Song(song))) = self.stack().current().selected_with_idx() {
269245
self.selected_song = Some((idx, song.as_path().to_owned()));
270246
}
247+
let playlist = playlist_name.to_owned();
271248
self.stack = new_stack;
272-
self.open_or_play(false, context, REINIT)?;
273249
self.stack_mut().current_mut().state.set_content_len(old_content_len);
274250
self.stack_mut().current_mut().state.set_viewport_len(old_viewport_len);
251+
252+
let songs =
253+
context.query_sync(move |client| Ok(client.list_playlist_info(&playlist, None)?))?;
254+
255+
self.stack_mut().push(songs.into_iter().map(DirOrSong::Song).collect());
256+
self.prepare_preview(context)?;
257+
if let Some((idx, song)) = &self.selected_song {
258+
let idx_to_select = self
259+
.stack
260+
.current()
261+
.items
262+
.iter()
263+
.find_position(|item| item.as_path() == song)
264+
.map_or(*idx, |(idx, _)| idx);
265+
self.stack.current_mut().state.set_viewport_len(old_viewport_len);
266+
self.stack
267+
.current_mut()
268+
.state
269+
.select(Some(idx_to_select), context.config.scrolloff);
270+
};
271+
self.stack_mut().clear_preview();
272+
self.prepare_preview(context)?;
273+
context.render()?;
275274
}
276275
[] => {
277276
let Some((selected_idx, selected_playlist)) = self

0 commit comments

Comments
 (0)