Skip to content

Commit 43f457d

Browse files
committed
api: implement GetTracklistTrack
readme: update usage instructions update README.md
1 parent c1679e0 commit 43f457d

File tree

10 files changed

+153
-36
lines changed

10 files changed

+153
-36
lines changed

Cargo.lock

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "music-player"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
edition = "2021"
55
repository = "https://github.com/tsirysndr/music-player"
66
license = "MIT"
@@ -20,11 +20,11 @@ path = "src/main.rs"
2020

2121
[dependencies.music-player-server]
2222
path = "server"
23-
version = "0.1.0"
23+
version = "0.1.2"
2424

2525
[dependencies.music-player-playback]
2626
path = "playback"
27-
version = "0.1.1"
27+
version = "0.1.2"
2828

2929
[dependencies.music-player-scanner]
3030
path = "scanner"
@@ -52,7 +52,7 @@ version = "0.1.0"
5252

5353
[dependencies.music-player-tracklist]
5454
path = "tracklist"
55-
version = "0.1.0"
55+
version = "0.1.1"
5656

5757
[dependencies.sea-orm-migration]
5858
version = "^0.9.0"

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,17 @@ Note: Don't forget to add `~/.cargo/bin` to your `PATH` environment variable.
4242
### macOS/Linux
4343

4444
```bash
45-
$ brew install tsirysndr/tap/musicplayer
45+
brew install tsirysndr/tap/musicplayer
4646
```
4747

4848
Or download the latest release for your platform [here](https://github.com/tsirysndr/music-player/releases).
4949

50+
## Start the server
51+
52+
```bash
53+
music-player
54+
```
55+
5056
## Usage
5157

5258
```

playback/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "music-player-playback"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
edition = "2021"
55
repository = "https://github.com/tsirysndr/music-player"
66
license = "MIT"
@@ -33,3 +33,4 @@ thiserror = "1.0.34"
3333
tokio = { version = "1.21.0", features = ["full"] }
3434
zerocopy = "0.6.1"
3535
owo-colors = "3.5.0"
36+
async-trait = "0.1.57"

playback/src/player.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use async_trait::async_trait;
12
use futures_util::{future::FusedFuture, Future};
23
use log::{error, trace};
34
use music_player_entity::track::Model as Track;
@@ -15,7 +16,10 @@ use std::{
1516
thread,
1617
};
1718
use symphonia::core::{errors::Error, io::MediaSourceStream, probe::Hint};
18-
use tokio::sync::mpsc;
19+
use tokio::{
20+
runtime::Runtime,
21+
sync::mpsc::{self, UnboundedReceiver},
22+
};
1923

2024
use crate::{
2125
audio_backend::Sink,
@@ -30,6 +34,7 @@ const PRELOAD_NEXT_TRACK_BEFORE_END: u64 = 30000;
3034

3135
pub type PlayerResult = Result<(), Error>;
3236

37+
#[async_trait]
3338
pub trait PlayerEngine: Send + Sync {
3439
fn load(&mut self, track_id: &str, _start_playing: bool, _position_ms: u32);
3540
fn load_tracklist(&mut self, tracks: Vec<Track>);
@@ -40,6 +45,11 @@ pub trait PlayerEngine: Send + Sync {
4045
fn seek(&self, position_ms: u32);
4146
fn next(&self);
4247
fn previous(&self);
48+
fn clear(&self);
49+
async fn get_tracks(&self) -> (Vec<Track>, Vec<Track>);
50+
async fn wait_for_tracklist(
51+
mut event: UnboundedReceiver<PlayerEvent>,
52+
) -> (Vec<Track>, Vec<Track>);
4353
}
4454

4555
#[derive(Clone)]
@@ -115,6 +125,7 @@ impl Player {
115125
}
116126
}
117127

128+
#[async_trait]
118129
impl PlayerEngine for Player {
119130
fn load(&mut self, track_id: &str, _start_playing: bool, _position_ms: u32) {
120131
self.command(PlayerCommand::Load {
@@ -153,6 +164,32 @@ impl PlayerEngine for Player {
153164
fn previous(&self) {
154165
self.command(PlayerCommand::Previous);
155166
}
167+
168+
fn clear(&self) {
169+
self.command(PlayerCommand::Clear);
170+
}
171+
172+
async fn get_tracks(&self) -> (Vec<Track>, Vec<Track>) {
173+
let channel = self.get_player_event_channel();
174+
let handle = thread::spawn(move || {
175+
Runtime::new()
176+
.unwrap()
177+
.block_on(Self::wait_for_tracklist(channel))
178+
});
179+
self.command(PlayerCommand::GetTracks);
180+
handle.join().unwrap()
181+
}
182+
183+
async fn wait_for_tracklist(
184+
mut channel: UnboundedReceiver<PlayerEvent>,
185+
) -> (Vec<Track>, Vec<Track>) {
186+
while let Some(event) = channel.recv().await {
187+
if matches!(event, PlayerEvent::TracklistUpdated { .. }) {
188+
return event.get_tracks().unwrap();
189+
}
190+
}
191+
(vec![], vec![])
192+
}
156193
}
157194

158195
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
@@ -303,6 +340,8 @@ impl PlayerInternal {
303340
PlayerCommand::AddEventSender(sender) => self.event_senders.push(sender),
304341
PlayerCommand::Next => self.handle_next(),
305342
PlayerCommand::Previous => self.handle_previous(),
343+
PlayerCommand::Clear => self.handle_clear(),
344+
PlayerCommand::GetTracks => self.handle_get_tracks(),
306345
}
307346
Ok(())
308347
}
@@ -430,6 +469,15 @@ impl PlayerInternal {
430469
self.handle_command_load(&self.tracklist.current_track().unwrap().uri);
431470
}
432471
}
472+
473+
fn handle_clear(&mut self) {
474+
self.tracklist.clear();
475+
}
476+
477+
fn handle_get_tracks(&mut self) {
478+
let tracks = self.tracklist.tracks();
479+
self.send_event(PlayerEvent::TracklistUpdated { tracks });
480+
}
433481
}
434482

435483
struct PlayerLoadedTrackData {
@@ -565,6 +613,8 @@ enum PlayerCommand {
565613
Next,
566614
Previous,
567615
AddEventSender(mpsc::UnboundedSender<PlayerEvent>),
616+
Clear,
617+
GetTracks,
568618
}
569619

570620
#[derive(Debug, Clone)]
@@ -579,6 +629,7 @@ pub enum PlayerEvent {
579629
EndOfTrack { is_last_track: bool },
580630
VolumeSet { volume: u16 },
581631
Error { track_id: String, error: String },
632+
TracklistUpdated { tracks: (Vec<Track>, Vec<Track>) },
582633
}
583634

584635
impl PlayerEvent {
@@ -589,6 +640,14 @@ impl PlayerEvent {
589640
_ => None,
590641
}
591642
}
643+
644+
pub fn get_tracks(&self) -> Option<(Vec<Track>, Vec<Track>)> {
645+
use PlayerEvent::*;
646+
match self {
647+
TracklistUpdated { tracks, .. } => Some(tracks.clone()),
648+
_ => None,
649+
}
650+
}
592651
}
593652

594653
pub type PlayerEventChannel = mpsc::UnboundedReceiver<PlayerEvent>;

server/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "music-player-server"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
edition = "2021"
55
repository = "https://github.com/tsirysndr/music-player"
66
license = "MIT"

server/proto/music/v1alpha1/tracklist.proto

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ message SetRepeatResponse {}
3636

3737
message ShuffleResponse {}
3838

39-
message GetTracklistTracksResponse {}
39+
message GetTracklistTracksResponse {
40+
repeated metadata.v1alpha1.Track next_tracks = 1;
41+
repeated metadata.v1alpha1.Track previous_tracks = 2;
42+
}
4043

4144
message GetRandomRequest {}
4245

server/src/tracklist.rs

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
use std::sync::Arc;
22

3-
use music_player_entity::{track, tracklist};
3+
use music_player_entity::track;
44
use music_player_playback::player::{Player, PlayerEngine};
55
use music_player_storage::Database;
6-
use sea_orm::EntityTrait;
76
use tokio::sync::Mutex;
87

9-
use crate::api::v1alpha1::{
10-
tracklist_service_server::TracklistService, AddTrackRequest, AddTrackResponse,
11-
ClearTracklistRequest, ClearTracklistResponse, FilterTracklistRequest, FilterTracklistResponse,
12-
GetNextTrackRequest, GetNextTrackResponse, GetPreviousTrackRequest, GetPreviousTrackResponse,
13-
GetRandomRequest, GetRandomResponse, GetRepeatRequest, GetRepeatResponse, GetSingleRequest,
14-
GetSingleResponse, GetTracklistTracksRequest, GetTracklistTracksResponse, PlayNextRequest,
15-
PlayNextResponse, RemoveTrackRequest, RemoveTrackResponse, SetRepeatRequest, SetRepeatResponse,
16-
ShuffleRequest, ShuffleResponse,
8+
use crate::{
9+
api::v1alpha1::{
10+
tracklist_service_server::TracklistService, AddTrackRequest, AddTrackResponse,
11+
ClearTracklistRequest, ClearTracklistResponse, FilterTracklistRequest,
12+
FilterTracklistResponse, GetNextTrackRequest, GetNextTrackResponse,
13+
GetPreviousTrackRequest, GetPreviousTrackResponse, GetRandomRequest, GetRandomResponse,
14+
GetRepeatRequest, GetRepeatResponse, GetSingleRequest, GetSingleResponse,
15+
GetTracklistTracksRequest, GetTracklistTracksResponse, PlayNextRequest, PlayNextResponse,
16+
RemoveTrackRequest, RemoveTrackResponse, SetRepeatRequest, SetRepeatResponse,
17+
ShuffleRequest, ShuffleResponse,
18+
},
19+
metadata::v1alpha1::{Album, Artist, Track},
1720
};
1821

1922
pub struct Tracklist {
@@ -34,8 +37,16 @@ impl TracklistService for Tracklist {
3437
request: tonic::Request<AddTrackRequest>,
3538
) -> Result<tonic::Response<AddTrackResponse>, tonic::Status> {
3639
let song = request.get_ref().track.as_ref().unwrap();
40+
let artist = match song.artists.len() {
41+
0 => "Unknown Artist".to_string(),
42+
_ => song.artists[0].name.clone(),
43+
};
3744
self.player.lock().await.load_tracklist(vec![track::Model {
45+
id: song.id.clone(),
3846
uri: song.uri.clone(),
47+
title: song.title.clone(),
48+
album: song.album.clone().unwrap_or_default().title,
49+
artist,
3950
..Default::default()
4051
}]);
4152
let response = AddTrackResponse {};
@@ -46,6 +57,7 @@ impl TracklistService for Tracklist {
4657
&self,
4758
_request: tonic::Request<ClearTracklistRequest>,
4859
) -> Result<tonic::Response<ClearTracklistResponse>, tonic::Status> {
60+
self.player.lock().await.clear();
4961
let response = ClearTracklistResponse {};
5062
Ok(tonic::Response::new(response))
5163
}
@@ -126,12 +138,50 @@ impl TracklistService for Tracklist {
126138
&self,
127139
_request: tonic::Request<GetTracklistTracksRequest>,
128140
) -> Result<tonic::Response<GetTracklistTracksResponse>, tonic::Status> {
129-
tracklist::Entity::find()
130-
.all(self.db.get_connection())
131-
.await
132-
.map_err(|e| tonic::Status::internal(e.to_string()))?;
133-
134-
let response = GetTracklistTracksResponse {};
141+
let (previous_tracks, next_tracks) = self.player.lock().await.get_tracks().await;
142+
143+
let response = GetTracklistTracksResponse {
144+
next_tracks: next_tracks
145+
.into_iter()
146+
.map(|track| Track {
147+
id: track.id,
148+
title: track.title,
149+
uri: track.uri,
150+
disc_number: format!("{}", track.track.unwrap_or(0)).parse().unwrap(),
151+
artists: vec![Artist {
152+
name: track.artist,
153+
..Default::default()
154+
}],
155+
album: Some(Album {
156+
// id: track.album_id.unwrap(),
157+
title: track.album,
158+
year: format!("{}", track.year.unwrap_or(0)).parse().unwrap(),
159+
..Default::default()
160+
}),
161+
..Default::default()
162+
})
163+
.collect(),
164+
previous_tracks: previous_tracks
165+
.into_iter()
166+
.map(|track| Track {
167+
id: track.id,
168+
title: track.title,
169+
uri: track.uri,
170+
disc_number: format!("{}", track.track.unwrap_or(0)).parse().unwrap(),
171+
artists: vec![Artist {
172+
name: track.artist,
173+
..Default::default()
174+
}],
175+
album: Some(Album {
176+
// id: track.album_id.unwrap(),
177+
title: track.album,
178+
year: format!("{}", track.year.unwrap_or(0)).parse().unwrap(),
179+
..Default::default()
180+
}),
181+
..Default::default()
182+
})
183+
.collect(),
184+
};
135185
Ok(tonic::Response::new(response))
136186
}
137187

tracklist/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "music-player-tracklist"
3-
version = "0.1.0"
3+
version = "0.1.1"
44
edition = "2021"
55
repository = "https://github.com/tsirysndr/music-player"
66
license = "MIT"

0 commit comments

Comments
 (0)