mecomp_core/
rpc.rs

1//! This module contains the service definitions.
2
3#![allow(clippy::future_not_send)]
4
5use std::{
6    net::{IpAddr, Ipv4Addr, SocketAddr},
7    ops::Range,
8    path::PathBuf,
9    time::Duration,
10};
11
12use mecomp_storage::db::schemas::{
13    album::{Album, AlbumBrief},
14    artist::{Artist, ArtistBrief},
15    collection::{Collection, CollectionBrief},
16    dynamic::{query::Query, DynamicPlaylist, DynamicPlaylistChangeSet},
17    playlist::{Playlist, PlaylistBrief},
18    song::{Song, SongBrief},
19    Thing,
20};
21use one_or_many::OneOrMany;
22use serde::{Deserialize, Serialize};
23use tarpc::{client, tokio_serde::formats::Json};
24
25use crate::{
26    errors::SerializableLibraryError,
27    state::{
28        library::{LibraryBrief, LibraryFull, LibraryHealth},
29        RepeatMode, SeekType, StateAudio,
30    },
31};
32
33pub type SongId = Thing;
34pub type ArtistId = Thing;
35pub type AlbumId = Thing;
36pub type CollectionId = Thing;
37pub type PlaylistId = Thing;
38pub type DynamicPlaylistId = Thing;
39
40#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
41pub struct SearchResult {
42    pub songs: Box<[Song]>,
43    pub albums: Box<[Album]>,
44    pub artists: Box<[Artist]>,
45}
46
47impl SearchResult {
48    #[must_use]
49    pub const fn len(&self) -> usize {
50        self.songs.len() + self.albums.len() + self.artists.len()
51    }
52
53    #[must_use]
54    pub const fn is_empty(&self) -> bool {
55        self.songs.is_empty() && self.albums.is_empty() && self.artists.is_empty()
56    }
57}
58
59// TODO: commands for reading songs by paths, artists by name, etc.
60
61/// The music player service, implemented by the music player daemon.
62#[tarpc::service]
63pub trait MusicPlayer {
64    /// Register a UDP listener with the daemon.
65    async fn register_listener(listener_addr: SocketAddr) -> ();
66
67    // misc
68    async fn ping() -> String;
69
70    // Music library.
71    /// Rescans the music library, only error is if a rescan is already in progress.
72    async fn library_rescan() -> Result<(), SerializableLibraryError>;
73    /// Check if a rescan is in progress.
74    async fn library_rescan_in_progress() -> bool;
75    /// Analyze the music library, only error is if an analysis is already in progress.
76    async fn library_analyze() -> Result<(), SerializableLibraryError>;
77    /// Check if an analysis is in progress.
78    async fn library_analyze_in_progress() -> bool;
79    /// Recluster the music library, only error is if a recluster is already in progress.
80    async fn library_recluster() -> Result<(), SerializableLibraryError>;
81    /// Check if a recluster is in progress.
82    async fn library_recluster_in_progress() -> bool;
83    /// Returns brief information about the music library.
84    async fn library_brief() -> Result<LibraryBrief, SerializableLibraryError>;
85    /// Returns full information about the music library. (all songs, artists, albums, etc.)
86    async fn library_full() -> Result<LibraryFull, SerializableLibraryError>;
87    /// Returns brief information about the music library's artists.
88    async fn library_artists_brief() -> Result<Box<[ArtistBrief]>, SerializableLibraryError>;
89    /// Returns full information about the music library's artists.
90    async fn library_artists_full() -> Result<Box<[Artist]>, SerializableLibraryError>;
91    /// Returns brief information about the music library's albums.
92    async fn library_albums_brief() -> Result<Box<[AlbumBrief]>, SerializableLibraryError>;
93    /// Returns full information about the music library's albums.
94    async fn library_albums_full() -> Result<Box<[Album]>, SerializableLibraryError>;
95    /// Returns brief information about the music library's songs.
96    async fn library_songs_brief() -> Result<Box<[SongBrief]>, SerializableLibraryError>;
97    /// Returns full information about the music library's songs.
98    async fn library_songs_full() -> Result<Box<[Song]>, SerializableLibraryError>;
99    /// Returns information about the health of the music library (are there any missing files, etc.)
100    async fn library_health() -> Result<LibraryHealth, SerializableLibraryError>;
101
102    // music library CRUD operations
103    /// Get a song by its ID.
104    async fn library_song_get(id: SongId) -> Option<Song>;
105    /// Get a song by its path.
106    async fn library_song_get_by_path(path: PathBuf) -> Option<Song>;
107    /// Get the artists of a song.
108    async fn library_song_get_artist(id: SongId) -> OneOrMany<Artist>;
109    /// Get the album of a song.
110    async fn library_song_get_album(id: SongId) -> Option<Album>;
111    /// Get the Playlists a song is in.
112    async fn library_song_get_playlists(id: SongId) -> Box<[Playlist]>;
113    /// Get the Collections a song is in.
114    async fn library_song_get_collections(id: SongId) -> Box<[Collection]>;
115    /// Get an album by its ID.
116    async fn library_album_get(id: AlbumId) -> Option<Album>;
117    /// Get the artists of an album
118    async fn library_album_get_artist(id: AlbumId) -> OneOrMany<Artist>;
119    /// Get the songs of an album
120    async fn library_album_get_songs(id: AlbumId) -> Option<Box<[Song]>>;
121    /// Get an artist by its ID.
122    async fn library_artist_get(id: ArtistId) -> Option<Artist>;
123    /// Get the songs of an artist
124    async fn library_artist_get_songs(id: ArtistId) -> Option<Box<[Song]>>;
125    /// Get the albums of an artist
126    async fn library_artist_get_albums(id: ArtistId) -> Option<Box<[Album]>>;
127
128    // Daemon control.
129    /// tells the daemon to shutdown.
130    async fn daemon_shutdown() -> ();
131
132    // State retrieval.
133    /// returns full information about the current state of the audio player (queue, current song, etc.)
134    async fn state_audio() -> Option<StateAudio>;
135
136    // Current (audio state)
137    /// returns the current artist.
138    async fn current_artist() -> OneOrMany<Artist>;
139    /// returns the current album.
140    async fn current_album() -> Option<Album>;
141    /// returns the current song.
142    async fn current_song() -> Option<Song>;
143
144    // Rand (audio state)
145    /// returns a random artist.
146    async fn rand_artist() -> Option<Artist>;
147    /// returns a random album.
148    async fn rand_album() -> Option<Album>;
149    /// returns a random song.
150    async fn rand_song() -> Option<Song>;
151
152    // Search (fuzzy keys)
153    /// returns a list of artists, albums, and songs matching the given search query.
154    async fn search(query: String, limit: u32) -> SearchResult;
155    /// returns a list of artists matching the given search query.
156    async fn search_artist(query: String, limit: u32) -> Box<[Artist]>;
157    /// returns a list of albums matching the given search query.
158    async fn search_album(query: String, limit: u32) -> Box<[Album]>;
159    /// returns a list of songs matching the given search query.
160    async fn search_song(query: String, limit: u32) -> Box<[Song]>;
161
162    // Playback control.
163    /// toggles playback (play/pause).
164    async fn playback_toggle() -> ();
165    /// start playback (unpause).
166    async fn playback_play() -> ();
167    /// pause playback.
168    async fn playback_pause() -> ();
169    /// stop playback.
170    async fn playback_stop() -> ();
171    /// restart the current song.
172    async fn playback_restart() -> ();
173    /// skip forward by the given amount of songs
174    async fn playback_skip_forward(amount: usize) -> ();
175    /// go backwards by the given amount of songs.
176    async fn playback_skip_backward(amount: usize) -> ();
177    /// only clear the player (i.e. stop playback)
178    async fn playback_clear_player() -> ();
179    /// clears the queue and stops playback.
180    async fn playback_clear() -> ();
181    /// seek forwards, backwards, or to an absolute second in the current song.
182    async fn playback_seek(seek: SeekType, duration: Duration) -> ();
183    /// set the repeat mode.
184    async fn playback_repeat(mode: RepeatMode) -> ();
185    /// Shuffle the current queue, then start playing from the 1st Song in the queue.
186    async fn playback_shuffle() -> ();
187    /// set the volume to the given value
188    /// The value `1.0` is the "normal" volume (unfiltered input). Any value other than `1.0` will multiply each sample by this value.
189    async fn playback_volume(volume: f32) -> ();
190    /// increase the volume by the given amount
191    async fn playback_volume_up(amount: f32) -> ();
192    /// decrease the volume by the given amount
193    async fn playback_volume_down(amount: f32) -> ();
194    /// toggle the volume mute.
195    async fn playback_volume_toggle_mute() -> ();
196    /// mute the volume.
197    async fn playback_mute() -> ();
198    /// unmute the volume.
199    async fn playback_unmute() -> ();
200
201    // Queue control.
202    /// add a thing to the queue.
203    /// (if the queue is empty, it will start playing the song.)
204    async fn queue_add(thing: Thing) -> Result<(), SerializableLibraryError>;
205    /// add a list of things to the queue.
206    /// (if the queue is empty, it will start playing the first thing in the list.)
207    async fn queue_add_list(list: Vec<Thing>) -> Result<(), SerializableLibraryError>;
208    /// set the current song to a queue index.
209    /// if the index is out of bounds, it will be clamped to the nearest valid index.
210    async fn queue_set_index(index: usize) -> ();
211    /// remove a range of songs from the queue.
212    /// if the range is out of bounds, it will be clamped to the nearest valid range.
213    async fn queue_remove_range(range: Range<usize>) -> ();
214
215    // Playlists.
216    /// Returns brief information about the users playlists.
217    async fn playlist_list() -> Box<[PlaylistBrief]>;
218    /// create a new playlist with the given name (if it does not already exist).
219    async fn playlist_get_or_create(name: String) -> Result<PlaylistId, SerializableLibraryError>;
220    /// remove a playlist.
221    async fn playlist_remove(id: PlaylistId) -> Result<(), SerializableLibraryError>;
222    /// clone a playlist.
223    /// (creates a new playlist with the same name (append "copy") and contents as the given playlist.)
224    /// returns the id of the new playlist.
225    async fn playlist_clone(id: PlaylistId) -> Result<PlaylistId, SerializableLibraryError>;
226    /// get the id of a playlist.
227    /// returns none if the playlist does not exist.
228    async fn playlist_get_id(name: String) -> Option<PlaylistId>;
229    /// remove a list of songs from a playlist.
230    /// if the songs are not in the playlist, this will do nothing.
231    async fn playlist_remove_songs(
232        playlist: PlaylistId,
233        songs: Vec<SongId>,
234    ) -> Result<(), SerializableLibraryError>;
235    /// Add a thing to a playlist.
236    /// If the thing is something that has songs (an album, artist, etc.), it will add all the songs.
237    async fn playlist_add(
238        playlist: PlaylistId,
239        thing: Thing,
240    ) -> Result<(), SerializableLibraryError>;
241    /// Add a list of things to a playlist.
242    /// If the things are something that have songs (an album, artist, etc.), it will add all the songs.
243    async fn playlist_add_list(
244        playlist: PlaylistId,
245        list: Vec<Thing>,
246    ) -> Result<(), SerializableLibraryError>;
247    /// Get a playlist by its ID.
248    async fn playlist_get(id: PlaylistId) -> Option<Playlist>;
249    /// Get the songs of a playlist
250    async fn playlist_get_songs(id: PlaylistId) -> Option<Box<[Song]>>;
251    /// Rename a playlist.
252    async fn playlist_rename(
253        id: PlaylistId,
254        name: String,
255    ) -> Result<Playlist, SerializableLibraryError>;
256
257    // Auto Curration commands.
258    // (collections, radios, smart playlists, etc.)
259    /// Collections: Return brief information about the users auto curration collections.
260    async fn collection_list() -> Box<[CollectionBrief]>;
261    /// Collections: get a collection by its ID.
262    async fn collection_get(id: CollectionId) -> Option<Collection>;
263    /// Collections: freeze a collection (convert it to a playlist).
264    async fn collection_freeze(
265        id: CollectionId,
266        name: String,
267    ) -> Result<PlaylistId, SerializableLibraryError>;
268    /// Get the songs of a collection
269    async fn collection_get_songs(id: CollectionId) -> Option<Box<[Song]>>;
270
271    // Radio commands.
272    /// Radio: get the `n` most similar songs to the given things.
273    async fn radio_get_similar(
274        things: Vec<Thing>,
275        n: u32,
276    ) -> Result<Box<[Song]>, SerializableLibraryError>;
277    /// Radio: get the ids of the `n` most similar songs to the given things.
278    async fn radio_get_similar_ids(
279        things: Vec<Thing>,
280        n: u32,
281    ) -> Result<Box<[SongId]>, SerializableLibraryError>;
282
283    // Dynamic playlist commands
284    /// Dynamic Playlists: create a new DP with the given name and query
285    async fn dynamic_playlist_create(
286        name: String,
287        query: Query,
288    ) -> Result<DynamicPlaylistId, SerializableLibraryError>;
289    /// Dynamic Playlists: list all DPs
290    async fn dynamic_playlist_list() -> Box<[DynamicPlaylist]>;
291    /// Dynamic Playlists: update a DP
292    async fn dynamic_playlist_update(
293        id: DynamicPlaylistId,
294        changes: DynamicPlaylistChangeSet,
295    ) -> Result<DynamicPlaylist, SerializableLibraryError>;
296    /// Dynamic Playlists: remove a DP
297    async fn dynamic_playlist_remove(id: DynamicPlaylistId)
298        -> Result<(), SerializableLibraryError>;
299    /// Dynamic Playlists: get a DP by its ID
300    async fn dynamic_playlist_get(id: DynamicPlaylistId) -> Option<DynamicPlaylist>;
301    /// Dynamic Playlists: get the songs of a DP
302    async fn dynamic_playlist_get_songs(id: DynamicPlaylistId) -> Option<Box<[Song]>>;
303}
304
305/// Initialize the music player client
306///
307/// # Errors
308///
309/// If the client cannot be initialized, an error is returned.
310pub async fn init_client(rpc_port: u16) -> Result<MusicPlayerClient, std::io::Error> {
311    let server_addr = (IpAddr::V4(Ipv4Addr::LOCALHOST), rpc_port);
312
313    let mut transport = tarpc::serde_transport::tcp::connect(server_addr, Json::default);
314    transport.config_mut().max_frame_length(usize::MAX);
315
316    // MusicPlayerClient is generated by the service attribute. It has a constructor `new` that takes a
317    // config and any Transport as input.
318    Ok(MusicPlayerClient::new(client::Config::default(), transport.await?).spawn())
319}