music_player_graphql/schema/
tracklist.rs1use async_graphql::*;
2use futures_util::Stream;
3use music_player_addons::{CurrentReceiverDevice, CurrentSourceDevice};
4use music_player_entity::{album as album_entity, artist as artist_entity, track as track_entity};
5use music_player_playback::player::PlayerCommand;
6use music_player_storage::repo::album::AlbumRepository;
7use music_player_storage::repo::artist::ArtistRepository;
8use music_player_storage::repo::playlist::PlaylistRepository;
9use music_player_storage::repo::track::TrackRepository;
10use music_player_storage::Database;
11use music_player_tracklist::Tracklist as TracklistState;
12use music_player_types::types::{self, RemoteCoverUrl, RemoteTrackUrl};
13use music_player_types::types::{CHROMECAST_DEVICE, MUSIC_PLAYER_DEVICE};
14use rand::seq::SliceRandom;
15use sea_orm::{
16 ColumnTrait, EntityTrait, JoinType, ModelTrait, QueryFilter, QueryOrder, QuerySelect,
17 RelationTrait,
18};
19use std::sync::Arc;
20use std::sync::Mutex as StdMutex;
21use tokio::sync::{mpsc::UnboundedSender, Mutex};
22use url::Url;
23
24use crate::load_tracks;
25use crate::simple_broker::SimpleBroker;
26use crate::update_cover_url;
27use crate::update_track_url;
28use crate::update_tracks_url;
29
30use super::objects::album::Album;
31use super::{
32 objects::{
33 track::{Track, TrackInput},
34 tracklist::Tracklist,
35 },
36 MutationType,
37};
38
39#[derive(Default)]
40pub struct TracklistQuery;
41
42#[Object]
43impl TracklistQuery {
44 async fn tracklist_tracks(&self, ctx: &Context<'_>) -> Result<Tracklist, Error> {
45 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
46 let state = ctx.data::<Arc<StdMutex<TracklistState>>>().unwrap();
47 let (previous_tracks, next_tracks) = state.lock().unwrap().tracks();
48 let mut device = current_device.lock().await;
49
50 if device.client.is_some() {
51 let receiver = device.client.as_mut().unwrap();
52 if receiver.device_type() == String::from(MUSIC_PLAYER_DEVICE) {
53 let (previous_tracks, next_tracks) = receiver.get_current_tracklist().await?;
54 return Ok(Tracklist {
55 next_tracks: next_tracks.into_iter().map(Into::into).collect(),
56 previous_tracks: previous_tracks.into_iter().map(Into::into).collect(),
57 });
58 }
59 let playback = receiver.get_current_playback().await?;
60 return Ok(playback.into());
61 }
62
63 let response = Tracklist {
64 next_tracks: next_tracks.into_iter().map(Into::into).collect(),
65 previous_tracks: previous_tracks.into_iter().map(Into::into).collect(),
66 };
67
68 Ok(response)
69 }
70 async fn get_repeat(&self, ctx: &Context<'_>) -> Result<bool, Error> {
71 todo!()
72 }
73 async fn get_random(&self, ctx: &Context<'_>) -> Result<bool, Error> {
74 todo!()
75 }
76 async fn get_next_track(&self, ctx: &Context<'_>) -> Result<Option<Track>, Error> {
77 todo!()
78 }
79
80 async fn get_previous_track(&self, ctx: &Context<'_>) -> Result<Option<Track>, Error> {
81 todo!()
82 }
83}
84
85#[derive(Default)]
86pub struct TracklistMutation;
87
88#[Object]
89impl TracklistMutation {
90 async fn add_track(&self, ctx: &Context<'_>, track: TrackInput) -> Result<Vec<Track>, Error> {
91 let state = ctx.data::<Arc<StdMutex<TracklistState>>>().unwrap();
92 let player_cmd = ctx
93 .data::<Arc<std::sync::Mutex<UnboundedSender<PlayerCommand>>>>()
94 .unwrap();
95 let db = ctx.data::<Database>().unwrap();
96 let current_device = ctx.data::<Arc<Mutex<CurrentSourceDevice>>>().unwrap();
97 let mut device = current_device.lock().await;
98
99 let id = track.id.to_string();
100
101 let track: track_entity::Model;
102
103 if device.client.is_some() {
104 let source = device.client.as_mut().unwrap();
105 let result = source.track(&id).await?;
106
107 let base_url = device
108 .source_device
109 .as_ref()
110 .unwrap()
111 .base_url
112 .as_ref()
113 .unwrap();
114
115 track = result
116 .with_remote_track_url(base_url.as_str())
117 .with_remote_cover_url(base_url.as_str())
118 .into();
119
120 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
121 let mut device = current_device.lock().await;
122
123 let receiver = device.client.as_mut();
124
125 if let Some(receiver) = receiver {
126 receiver.load(track.clone().into()).await?;
127 } else {
128 player_cmd
129 .lock()
130 .unwrap()
131 .send(PlayerCommand::LoadTracklist {
132 tracks: vec![track.clone()],
133 })
134 .unwrap();
135 }
136
137 return Ok(vec![track.clone().into()]);
138 }
139
140 let result: Vec<(track_entity::Model, Vec<artist_entity::Model>)> =
141 track_entity::Entity::find_by_id(id.clone())
142 .find_with_related(artist_entity::Entity)
143 .all(db.get_connection())
144 .await?;
145
146 if result.len() == 0 {
147 return Err(Error::new("Track not found"));
148 }
149
150 let (mut track, artists) = result.into_iter().next().unwrap();
151 track.artists = artists;
152
153 let result: Vec<(track_entity::Model, Option<album_entity::Model>)> =
154 track_entity::Entity::find_by_id(id.clone())
155 .find_also_related(album_entity::Entity)
156 .all(db.get_connection())
157 .await?;
158 let (_, album) = result.into_iter().next().unwrap();
159 track.album = album.unwrap();
160
161 player_cmd
162 .lock()
163 .unwrap()
164 .send(PlayerCommand::LoadTracklist {
165 tracks: vec![track.clone()],
166 })
167 .unwrap();
168
169 let (previous_tracks, next_tracks) = state.lock().unwrap().tracks();
170
171 SimpleBroker::publish(TracklistChanged {
172 tracklist: Tracklist {
173 next_tracks: next_tracks.into_iter().map(Into::into).collect(),
174 previous_tracks: previous_tracks.into_iter().map(Into::into).collect(),
175 },
176 mutation_type: MutationType::Updated,
177 track: Some(track.clone().into()),
178 });
179 Ok(vec![])
180 }
181
182 async fn add_tracks(&self, ctx: &Context<'_>, tracks: Vec<TrackInput>) -> Result<bool, Error> {
183 let _player_cmd = ctx
184 .data::<Arc<std::sync::Mutex<UnboundedSender<PlayerCommand>>>>()
185 .unwrap();
186 todo!()
187 }
188
189 async fn clear_tracklist(&self, ctx: &Context<'_>) -> Result<bool, Error> {
190 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
191 let mut device = current_device.lock().await;
192
193 if device.client.is_some() {
194 let receiver = device.client.as_mut().unwrap();
195 return Ok(true);
196 }
197
198 let player_cmd = ctx
199 .data::<Arc<StdMutex<UnboundedSender<PlayerCommand>>>>()
200 .unwrap();
201 player_cmd
202 .lock()
203 .unwrap()
204 .send(PlayerCommand::Clear)
205 .unwrap();
206 SimpleBroker::publish(TracklistChanged {
207 tracklist: Tracklist {
208 next_tracks: vec![],
209 previous_tracks: vec![],
210 },
211 mutation_type: MutationType::Cleared,
212 track: None,
213 });
214 Ok(true)
215 }
216
217 async fn remove_track(&self, ctx: &Context<'_>, position: u32) -> Result<bool, Error> {
218 let state = ctx.data::<Arc<StdMutex<TracklistState>>>().unwrap();
219 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
220 let mut device = current_device.lock().await;
221
222 if device.client.is_some() {
223 let receiver = device.client.as_mut().unwrap();
224 receiver.remove_track_at(position).await?;
225 return Ok(true);
226 }
227
228 let player_cmd = ctx
229 .data::<Arc<std::sync::Mutex<UnboundedSender<PlayerCommand>>>>()
230 .unwrap();
231 player_cmd
232 .lock()
233 .unwrap()
234 .send(PlayerCommand::RemoveTrack(position as usize))
235 .unwrap();
236
237 let (previous_tracks, next_tracks) = state.lock().unwrap().tracks();
238 SimpleBroker::publish(TracklistChanged {
239 tracklist: Tracklist {
240 next_tracks: next_tracks.into_iter().map(Into::into).collect(),
241 previous_tracks: previous_tracks.into_iter().map(Into::into).collect(),
242 },
243 mutation_type: MutationType::Updated,
244 track: None,
245 });
246 Ok(true)
247 }
248
249 async fn play_track_at(&self, ctx: &Context<'_>, position: u32) -> Result<bool, Error> {
250 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
251 let mut device = current_device.lock().await;
252
253 if device.client.is_some() {
254 let receiver = device.client.as_mut().unwrap();
255 receiver.play_track_at(position).await?;
256 return Ok(true);
257 }
258
259 let player_cmd = ctx
260 .data::<Arc<StdMutex<UnboundedSender<PlayerCommand>>>>()
261 .unwrap();
262 player_cmd
263 .lock()
264 .unwrap()
265 .send(PlayerCommand::PlayTrackAt(position as usize))
266 .unwrap();
267 Ok(true)
268 }
269
270 async fn shuffle(&self, ctx: &Context<'_>) -> Result<bool, Error> {
271 let _player_cmd = ctx
272 .data::<Arc<StdMutex<UnboundedSender<PlayerCommand>>>>()
273 .unwrap();
274 todo!()
275 }
276
277 async fn play_next(&self, ctx: &Context<'_>, id: ID) -> Result<bool, Error> {
278 let db = ctx.data::<Database>().unwrap();
279 let devices = ctx.data::<Arc<StdMutex<Vec<types::Device>>>>().unwrap();
280 let devices = devices.lock().unwrap().clone();
281 let current_device = ctx.data::<Arc<Mutex<CurrentSourceDevice>>>().unwrap();
282 let mut device = current_device.lock().await;
283
284 let id = id.to_string();
285
286 let mut track: track_entity::Model;
287
288 if device.client.is_some() {
289 let source = device.client.as_mut().unwrap();
290 let source_ip = source.device_ip();
291 let result = source.track(&id).await?;
292
293 let base_url = device
294 .source_device
295 .as_ref()
296 .unwrap()
297 .base_url
298 .as_ref()
299 .unwrap();
300
301 track = result
302 .with_remote_track_url(base_url.as_str())
303 .with_remote_cover_url(base_url.as_str())
304 .into();
305
306 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
307 let mut device = current_device.lock().await;
308
309 let receiver = device.client.as_mut().unwrap();
310 let will_play_on_chromecast = receiver.device_type() == String::from(CHROMECAST_DEVICE);
311 if will_play_on_chromecast {
312 let url = Url::parse(track.uri.as_str()).unwrap();
313 let host = url.host_str().unwrap();
314 track.uri = track.uri.to_lowercase().replace(host, source_ip.as_str());
315 let cover = match track.clone().album.cover {
316 Some(cover) => Url::parse(cover.as_str()).ok().map(|url| {
317 let host = url.host_str().unwrap();
318 cover.to_lowercase().replace(host, source_ip.as_str())
319 }),
320 None => None,
321 };
322 track.album.cover = cover;
323 }
324 receiver.play_next(track.into()).await?;
325 return Ok(true);
326 } else {
327 track = TrackRepository::new(db.get_connection()).find(&id).await?;
328 }
329
330 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
331 let mut device = current_device.lock().await;
332
333 if device.client.is_some() {
334 let receiver = device.client.as_mut().unwrap();
335 let will_play_on_chromecast = receiver.device_type() == String::from(CHROMECAST_DEVICE);
336 track = update_track_url(devices.clone(), track, will_play_on_chromecast)?;
337 let t: types::Track = track.into();
338 track = update_cover_url(devices.clone(), t.clone(), will_play_on_chromecast)
339 .unwrap_or_else(|_| t.clone())
340 .into();
341 receiver.play_next(track.into()).await?;
342 return Ok(true);
343 }
344
345 let player_cmd = ctx
346 .data::<Arc<StdMutex<UnboundedSender<PlayerCommand>>>>()
347 .unwrap();
348 player_cmd
349 .lock()
350 .unwrap()
351 .send(PlayerCommand::PlayNext(track_entity::Model { ..track }))
352 .unwrap();
353 Ok(true)
354 }
355
356 async fn play_album(
357 &self,
358 ctx: &Context<'_>,
359 id: ID,
360 position: Option<u32>,
361 shuffle: bool,
362 ) -> Result<bool, Error> {
363 let player_cmd = ctx
364 .data::<Arc<StdMutex<UnboundedSender<PlayerCommand>>>>()
365 .unwrap();
366 let db = ctx.data::<Database>().unwrap();
367 let devices = ctx.data::<Arc<StdMutex<Vec<types::Device>>>>().unwrap();
368 let devices = devices.lock().unwrap().clone();
369 let current_device = ctx.data::<Arc<Mutex<CurrentSourceDevice>>>().unwrap();
370 let mut device = current_device.lock().await;
371
372 let id = id.to_string();
373
374 if device.client.is_some() {
375 let source = device.client.as_mut().unwrap();
376 let album = source.album(&id).await?;
377 let source_ip = source.device_ip();
378
379 let base_url = device
380 .source_device
381 .as_ref()
382 .unwrap()
383 .base_url
384 .as_ref()
385 .unwrap();
386
387 let album: album_entity::Model = album
388 .with_remote_cover_url(&base_url)
389 .with_remote_track_url(&base_url)
390 .into();
391 let tracks = album.tracks;
392
393 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
394 let mut device = current_device.lock().await;
395 let receiver = device.client.as_mut();
396
397 load_tracks(
398 player_cmd,
399 receiver,
400 Some(source_ip),
401 tracks,
402 position,
403 shuffle,
404 )
405 .await?;
406 return Ok(true);
407 }
408
409 let mut result = AlbumRepository::new(db.get_connection()).find(&id).await?;
410
411 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
412 let mut device = current_device.lock().await;
413
414 if device.client.is_some() {
415 let receiver = device.client.as_mut().unwrap();
416 let will_play_on_chromecast = receiver.device_type() == String::from(CHROMECAST_DEVICE);
417 result = update_tracks_url(devices.clone(), result, will_play_on_chromecast)?;
418 result.tracks = result
419 .tracks
420 .into_iter()
421 .map(|track| {
422 let t: types::Track = track.into();
423 update_cover_url(devices.clone(), t.clone(), will_play_on_chromecast)
424 .unwrap_or_else(|_| t.clone())
425 .into()
426 })
427 .collect();
428 }
429
430 load_tracks(
431 player_cmd,
432 device.client.as_mut(),
433 None,
434 result.tracks,
435 position,
436 shuffle,
437 )
438 .await?;
439 Ok(true)
440 }
441
442 async fn play_artist_tracks(
443 &self,
444 ctx: &Context<'_>,
445 id: ID,
446 position: Option<u32>,
447 shuffle: bool,
448 ) -> Result<bool, Error> {
449 let player_cmd = ctx
450 .data::<Arc<StdMutex<UnboundedSender<PlayerCommand>>>>()
451 .unwrap();
452 let db = ctx.data::<Database>().unwrap();
453 let devices = ctx.data::<Arc<StdMutex<Vec<types::Device>>>>().unwrap();
454 let devices = devices.lock().unwrap().clone();
455 let current_device = ctx.data::<Arc<Mutex<CurrentSourceDevice>>>().unwrap();
456 let mut device = current_device.lock().await;
457 let id = id.to_string();
458
459 if device.client.is_some() {
460 let source = device.client.as_mut().unwrap();
461 let artist = source.artist(&id).await?;
462 let source_ip = source.device_ip();
463
464 let base_url = device
465 .source_device
466 .as_ref()
467 .unwrap()
468 .base_url
469 .as_ref()
470 .unwrap();
471
472 let artist: artist_entity::Model =
473 artist.with_remote_track_url(base_url.as_str()).into();
474
475 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
476 let mut device = current_device.lock().await;
477 let receiver = device.client.as_mut();
478
479 load_tracks(
480 player_cmd,
481 receiver,
482 Some(source_ip),
483 artist.tracks,
484 position,
485 shuffle,
486 )
487 .await?;
488 return Ok(true);
489 }
490
491 let mut artist = ArtistRepository::new(db.get_connection()).find(&id).await?;
492
493 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
494 let mut device = current_device.lock().await;
495
496 if device.client.is_some() {
497 let receiver = device.client.as_mut().unwrap();
498 let will_play_on_chromecast = receiver.device_type() == String::from(CHROMECAST_DEVICE);
499 artist = update_tracks_url(devices.clone(), artist, will_play_on_chromecast)?;
500 artist.tracks = artist
501 .tracks
502 .into_iter()
503 .map(|track| {
504 let t: types::Track = track.into();
505 update_cover_url(devices.clone(), t.clone(), will_play_on_chromecast)
506 .unwrap_or_else(|_| t.clone())
507 .into()
508 })
509 .collect();
510 }
511
512 load_tracks(
513 player_cmd,
514 device.client.as_mut(),
515 None,
516 artist.tracks,
517 position,
518 shuffle,
519 )
520 .await?;
521 Ok(true)
522 }
523
524 async fn play_playlist(
525 &self,
526 ctx: &Context<'_>,
527 id: ID,
528 position: Option<u32>,
529 shuffle: bool,
530 ) -> Result<bool, Error> {
531 let player_cmd = ctx
532 .data::<Arc<std::sync::Mutex<UnboundedSender<PlayerCommand>>>>()
533 .unwrap();
534 let db = ctx.data::<Database>().unwrap();
535 let devices = ctx.data::<Arc<StdMutex<Vec<types::Device>>>>().unwrap();
536 let devices = devices.lock().unwrap().clone();
537 let current_device = ctx.data::<Arc<Mutex<CurrentSourceDevice>>>().unwrap();
538 let mut device = current_device.lock().await;
539
540 let id = id.to_string();
541
542 if device.client.is_some() {
543 let source = device.client.as_mut().unwrap();
544 let result = source.playlist(&id).await?;
545 let source_ip = source.device_ip();
546
547 let base_url = device
548 .source_device
549 .as_ref()
550 .unwrap()
551 .base_url
552 .as_ref()
553 .unwrap();
554
555 let tracks = result.with_remote_track_url(base_url.as_str()).tracks;
556 let tracks: Vec<track_entity::Model> = tracks.into_iter().map(Into::into).collect();
557
558 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
559 let mut device = current_device.lock().await;
560 let receiver = device.client.as_mut();
561
562 load_tracks(
563 player_cmd,
564 receiver,
565 Some(source_ip),
566 tracks,
567 position,
568 shuffle,
569 )
570 .await?;
571 return Ok(true);
572 }
573
574 let mut playlist = PlaylistRepository::new(db.get_connection())
575 .find(id.as_str())
576 .await?;
577
578 let current_device = ctx.data::<Arc<Mutex<CurrentReceiverDevice>>>().unwrap();
579 let mut device = current_device.lock().await;
580
581 if device.client.is_some() {
582 let receiver = device.client.as_mut().unwrap();
583 let will_play_on_chromecast = receiver.device_type() == String::from(CHROMECAST_DEVICE);
584 playlist = update_tracks_url(devices.clone(), playlist, will_play_on_chromecast)?;
585 playlist.tracks = playlist
586 .tracks
587 .into_iter()
588 .map(|track| {
589 let t: types::Track = track.into();
590 update_cover_url(devices.clone(), t.clone(), will_play_on_chromecast)
591 .unwrap_or_else(|_| t.clone())
592 .into()
593 })
594 .collect();
595 }
596
597 let tracks: Vec<track_entity::Model> =
598 playlist.tracks.into_iter().map(Into::into).collect();
599
600 load_tracks(
601 player_cmd,
602 device.client.as_mut(),
603 None,
604 tracks,
605 position,
606 shuffle,
607 )
608 .await?;
609
610 Ok(true)
611 }
612}
613
614#[derive(Clone)]
615struct TracklistChanged {
616 mutation_type: MutationType,
617 tracklist: Tracklist,
618 track: Option<Track>,
619}
620
621#[Object]
622impl TracklistChanged {
623 async fn mutation_type(&self) -> MutationType {
624 self.mutation_type
625 }
626
627 async fn tracklist(&self) -> &Tracklist {
628 &self.tracklist
629 }
630
631 async fn track(&self) -> Option<&Track> {
632 self.track.as_ref()
633 }
634}
635
636#[derive(Default)]
637pub struct TracklistSubscription;
638
639#[Subscription]
640impl TracklistSubscription {
641 async fn tracklist(&self, _id: ID) -> impl Stream<Item = TracklistChanged> {
642 SimpleBroker::<TracklistChanged>::subscribe()
643 }
644}