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