+1
-1
src/app.rs
+1
-1
src/app.rs
+47
-1
src/interactive.rs
+47
-1
src/interactive.rs
···
7
7
use ratatui::prelude::*;
8
8
use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph};
9
9
use tokio::sync::mpsc;
10
+
use tunein_cli::os_media_controls::{self, OsMediaControls};
10
11
12
+
use crate::app::send_os_media_controls_command;
11
13
use crate::audio::{AudioController, PlaybackEvent, PlaybackState};
12
14
use crate::extract::get_currently_playing;
13
15
use crate::favorites::{FavoriteStation, FavoritesStore};
···
42
44
let (input_tx, mut input_rx) = mpsc::unbounded_channel();
43
45
spawn_input_thread(input_tx.clone());
44
46
47
+
let os_media_controls = OsMediaControls::new()
48
+
.inspect_err(|err| {
49
+
eprintln!(
50
+
"error: failed to initialize os media controls due to `{}`",
51
+
err
52
+
);
53
+
})
54
+
.ok();
55
+
45
56
let mut app = HubApp::new(
46
57
provider_name.to_string(),
47
58
provider,
48
59
audio,
49
60
favorites,
50
61
metadata_tx,
62
+
os_media_controls,
51
63
);
52
64
53
65
let result = loop {
···
103
115
metadata_tx: mpsc::UnboundedSender<HubMessage>,
104
116
now_playing_station_id: Option<String>,
105
117
next_now_playing_poll: Instant,
118
+
os_media_controls: Option<OsMediaControls>,
106
119
}
107
120
108
121
impl HubApp {
···
112
125
audio: AudioController,
113
126
favorites: FavoritesStore,
114
127
metadata_tx: mpsc::UnboundedSender<HubMessage>,
128
+
os_media_controls: Option<OsMediaControls>,
115
129
) -> Self {
116
130
let mut ui = UiState::default();
117
131
ui.menu_state.select(Some(0));
···
129
143
metadata_tx,
130
144
now_playing_station_id: None,
131
145
next_now_playing_poll: Instant::now(),
146
+
os_media_controls,
132
147
}
133
148
}
134
149
···
202
217
})
203
218
})
204
219
.unwrap_or_else(|| "Unknown".to_string());
205
-
self.render_labeled_line(frame, area, row, "Station ", &station_name);
220
+
let station_id = self
221
+
.current_playback
222
+
.as_ref()
223
+
.map(|p| p.station.id.as_str())
224
+
.or_else(|| self.current_station.as_ref().map(|s| s.station.id.as_str()))
225
+
.unwrap_or("N/A");
226
+
227
+
self.render_labeled_line(
228
+
frame,
229
+
area,
230
+
row,
231
+
"Station ",
232
+
&format!("{} - {}", station_name, station_id),
233
+
);
206
234
row += 1;
207
235
208
236
let now_playing = self
···
969
997
self.current_playback = Some(state.clone());
970
998
if let Some(station) = self.current_station.as_mut() {
971
999
station.station.playing = Some(state.now_playing.clone());
1000
+
station.station.id = state.station.id.clone();
972
1001
}
973
1002
self.set_status(&format!("Now playing {}", state.stream_name));
974
1003
self.prepare_now_playing_poll();
···
996
1025
station.station.playing = Some(now_playing.clone());
997
1026
}
998
1027
self.set_status(&format!("Now Playing {}", now_playing));
1028
+
1029
+
let name = self
1030
+
.current_station
1031
+
.as_ref()
1032
+
.map(|s| s.station.name.clone())
1033
+
.unwrap_or_default();
1034
+
1035
+
send_os_media_controls_command(
1036
+
self.os_media_controls.as_mut(),
1037
+
os_media_controls::Command::SetMetadata(souvlaki::MediaMetadata {
1038
+
title: (!now_playing.is_empty()).then_some(now_playing.as_str()),
1039
+
album: (!name.is_empty()).then_some(name.as_str()),
1040
+
artist: None,
1041
+
cover_url: None,
1042
+
duration: None,
1043
+
}),
1044
+
);
999
1045
}
1000
1046
}
1001
1047
}
+6
-1
src/provider/tunein.rs
+6
-1
src/provider/tunein.rs
···
55
55
None => Ok(None),
56
56
}
57
57
}
58
-
_ => Ok(Some(Station::from(stations[0].clone()))),
58
+
_ => {
59
+
let mut station = Station::from(stations[0].clone());
60
+
// Preserve the original station ID since StationLinkDetails doesn't contain it
61
+
station.id = id;
62
+
Ok(Some(station))
63
+
}
59
64
}
60
65
}
61
66