media_remote/high_level/
now_playing.rs1use std::{
2 collections::HashMap,
3 io::Cursor,
4 sync::{atomic::AtomicU64, Arc, Mutex, RwLock, RwLockReadGuard},
5 time::SystemTime,
6};
7
8use image::ImageReader;
9
10use crate::{
11 add_observer, get_bundle_info, get_now_playing_application_is_playing,
12 get_now_playing_client_bundle_identifier, get_now_playing_client_parent_app_bundle_identifier,
13 get_now_playing_info, register_for_now_playing_notifications, remove_observer,
14 unregister_for_now_playing_notifications, InfoTypes, Notification, NowPlayingInfo, Number,
15 Observer, Subscription,
16};
17
18use crate::{Controller, ListenerToken};
19
20pub struct NowPlaying {
34 info: Arc<RwLock<Option<NowPlayingInfo>>>,
35 observers: Vec<Observer>,
36 listeners: Arc<
37 Mutex<
38 HashMap<
39 ListenerToken,
40 Box<dyn Fn(RwLockReadGuard<'_, Option<NowPlayingInfo>>) + Send + Sync>,
41 >,
42 >,
43 >,
44 token_counter: Arc<AtomicU64>,
45}
46
47fn update_all(info: Arc<RwLock<Option<NowPlayingInfo>>>) {
48 let mut info_guard = info.write().unwrap();
49 *info_guard = Some(NowPlayingInfo {
50 is_playing: None,
51 title: None,
52 artist: None,
53 album: None,
54 album_cover: None,
55 elapsed_time: None,
56 duration: None,
57 info_update_time: None,
58 bundle_id: None,
59 bundle_name: None,
60 bundle_icon: None,
61 });
62 drop(info_guard);
63
64 update_state(info.clone());
65 update_app(info.clone());
66 update_info(info.clone());
67}
68
69fn update_state(info: Arc<RwLock<Option<NowPlayingInfo>>>) {
70 let mut info_guard = info.write().unwrap();
71 if info_guard.as_ref().is_none() {
72 drop(info_guard);
73 return update_all(info.clone());
74 }
75
76 let is_playing = get_now_playing_application_is_playing();
77 if let Some(is_playing) = is_playing {
78 info_guard.as_mut().unwrap().is_playing = Some(is_playing);
79 }
80}
81
82fn update_info(info: Arc<RwLock<Option<NowPlayingInfo>>>) {
83 let mut info_guard = info.write().unwrap();
84 if info_guard.as_ref().is_none() {
85 drop(info_guard);
86 return update_all(info.clone());
87 }
88
89 let now_playing_info = get_now_playing_info();
90 if let Some(info) = now_playing_info {
91 macro_rules! update_string_info {
92 ($key:expr, $field:expr) => {
93 if let Some(InfoTypes::String(s)) = info.get($key) {
94 if !s.is_empty() {
95 $field = Some(s.clone());
96 }
97 }
98 };
99 }
100
101 update_string_info!(
102 "kMRMediaRemoteNowPlayingInfoTitle",
103 info_guard.as_mut().unwrap().title
104 );
105 update_string_info!(
106 "kMRMediaRemoteNowPlayingInfoArtist",
107 info_guard.as_mut().unwrap().artist
108 );
109 update_string_info!(
110 "kMRMediaRemoteNowPlayingInfoAlbum",
111 info_guard.as_mut().unwrap().album
112 );
113
114 macro_rules! update_float_info {
115 ($key:expr, $field:expr) => {
116 if let Some(InfoTypes::Number(Number::Floating(f))) = info.get($key) {
117 $field = Some(f.clone());
118 }
119 };
120 }
121
122 update_float_info!(
123 "kMRMediaRemoteNowPlayingInfoDuration",
124 info_guard.as_mut().unwrap().duration
125 );
126 update_float_info!(
127 "kMRMediaRemoteNowPlayingInfoElapsedTime",
128 info_guard.as_mut().unwrap().elapsed_time
129 );
130
131 if let Some(InfoTypes::Data(d)) = info.get("kMRMediaRemoteNowPlayingInfoArtworkData") {
132 info_guard.as_mut().unwrap().album_cover = ImageReader::new(Cursor::new(d))
133 .with_guessed_format()
134 .ok()
135 .and_then(|img| img.decode().ok());
136 }
137
138 info_guard.as_mut().unwrap().info_update_time = info
139 .get("kMRMediaRemoteNowPlayingInfoTimestamp")
140 .and_then(|f| match f {
141 InfoTypes::SystemTime(t) => Some(t.clone()),
142 _ => None,
143 })
144 .or(Some(SystemTime::now()));
145 }
146}
147
148fn update_app(info: Arc<RwLock<Option<NowPlayingInfo>>>) {
149 let mut info_guard = info.write().unwrap();
150 if info_guard.as_ref().is_none() {
151 drop(info_guard);
152 return update_all(info.clone());
153 }
154
155 let mut bundle_id = get_now_playing_client_parent_app_bundle_identifier();
156 if bundle_id.is_none() {
157 bundle_id = get_now_playing_client_bundle_identifier();
158 }
159
160 if let Some(id) = bundle_id {
161 let bundle_info = get_bundle_info(id.as_str());
162 if let Some(info) = bundle_info {
163 info_guard.as_mut().unwrap().bundle_id = Some(id);
164 info_guard.as_mut().unwrap().bundle_name = Some(info.name);
165 info_guard.as_mut().unwrap().bundle_icon = Some(info.icon);
166 }
167 }
168}
169
170impl NowPlaying {
171 fn register(&mut self) {
172 register_for_now_playing_notifications();
173
174 let info = Arc::clone(&self.info);
176 update_all(info.clone());
177
178 macro_rules! add_observer_macro {
179 ($notification:expr, $update_fn:expr) => {{
180 let info = Arc::clone(&self.info);
181 let listeners = Arc::clone(&self.listeners);
182
183 self.observers.push(add_observer($notification, move || {
184 $update_fn(info.clone());
185 for (_, listener) in listeners.clone().lock().unwrap().iter() {
186 listener(info.read().unwrap());
187 }
188 }));
189 }};
190 }
191
192 add_observer_macro!(Notification::NowPlayingApplicationDidChange, update_app);
193
194 add_observer_macro!(Notification::NowPlayingInfoDidChange, update_info);
195 add_observer_macro!(
203 Notification::NowPlayingApplicationIsPlayingDidChange,
204 update_state
205 );
206 }
207
208 pub fn new() -> Self {
223 let mut new_instance = Self {
224 info: Arc::new(RwLock::new(None)),
225 observers: vec![],
226 listeners: Arc::new(Mutex::new(HashMap::new())),
227 token_counter: Arc::new(AtomicU64::new(0)),
228 };
229
230 new_instance.register();
231
232 new_instance
233 }
234
235 pub fn get_info(&self) -> RwLockReadGuard<'_, Option<NowPlayingInfo>> {
260 let mut info_guard = self.info.write().unwrap();
261 let info = info_guard.as_mut();
262
263 if info.is_some() {
264 let info = info.unwrap();
265 if info.is_playing.is_some_and(|x| x)
266 && info.elapsed_time.is_some()
267 && info.info_update_time.is_some()
268 {
269 info.elapsed_time = Some(
270 info.elapsed_time.unwrap()
271 + info.info_update_time.unwrap().elapsed().unwrap().as_secs() as f64,
272 );
273 info.info_update_time = Some(SystemTime::now())
274 }
275 }
276
277 drop(info_guard);
278
279 self.info.read().unwrap()
280 }
281}
282
283impl Drop for NowPlaying {
284 fn drop(&mut self) {
285 while let Some(observer) = self.observers.pop() {
286 remove_observer(observer);
287 }
288
289 unregister_for_now_playing_notifications();
290 }
291}
292
293impl Controller for NowPlaying {
294 fn is_info_some(&self) -> bool {
295 self.info.read().unwrap().as_ref().is_some()
296 }
297}
298
299impl Subscription for NowPlaying {
300 fn get_info(&self) -> RwLockReadGuard<'_, Option<NowPlayingInfo>> {
301 self.get_info()
302 }
303
304 fn get_token_counter(&self) -> Arc<AtomicU64> {
305 self.token_counter.clone()
306 }
307
308 fn get_listeners(
309 &self,
310 ) -> Arc<
311 Mutex<
312 HashMap<
313 super::subscription::ListenerToken,
314 Box<dyn Fn(RwLockReadGuard<'_, Option<NowPlayingInfo>>) + Send + Sync>,
315 >,
316 >,
317 > {
318 self.listeners.clone()
319 }
320}