music_player_graphql/schema/
tracklist.rs

1use 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}