Skip to main content

chilen_ipc/
library.rs

1use std::{path::PathBuf, time::Duration};
2
3use serde::{Deserialize, Serialize};
4
5// TODO: Add more tags for this
6/// Struct representing a track from the [music library](MusicLibrary).
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct Track {
9    /// The path to the audio file.
10    pub path: PathBuf,
11    /// The path to the extracted cover file.
12    pub cover_path: Option<PathBuf>,
13    /// The duration of the track.
14    pub duration: Duration,
15    // TODO: Add an option to split the tag with separators like ",", ";", "/", etc.
16    /// The track artist.
17    pub artist: Option<String>,
18    /// The track title.
19    pub title: Option<String>,
20    /// The track album.
21    pub album: Option<String>,
22    // TODO: Same as with artist
23    /// The track genre.
24    pub genre: Option<String>,
25    // TODO: Add easy access to synced lyrics
26    /// Possibly synchronized lyrics text.
27    pub lyrics: Option<String>,
28    /// Contents of the comment tag.
29    pub comment: Option<String>,
30    pub track: Option<u32>,
31    pub track_total: Option<u32>,
32    pub disc: Option<u32>,
33    pub disc_total: Option<u32>,
34    /// Release year.
35    pub year: Option<u32>,
36}
37
38impl std::fmt::Display for Track {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        write!(
41            f,
42            "{} - {} ({})",
43            self.artist.clone().unwrap_or(String::from("Unknown")),
44            self.title.clone().unwrap_or(String::from("Unknown")),
45            self.path.to_string_lossy()
46        )
47    }
48}
49
50/// Struct representing a playlist in the [music library](MusicLibrary).
51#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
52pub struct Playlist {
53    /// The name of the playlist.
54    ///
55    /// Playlist names are unique.
56    pub name: String,
57    /// The content of the playlist.
58    ///
59    /// All tracks must already be in the [music library](MusicLibrary). If a track is removed from
60    /// the library (eg. by removing an audio file from the music directory and reloading the
61    /// library), it will also be removed from all the playlists.
62    pub tracks: Vec<Track>,
63}
64
65/// Struct representing the contents of the music library.
66#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
67pub struct MusicLibrary {
68    /// The list of playlists in the music library.
69    pub playlists: Vec<Playlist>,
70    /// The list of all tracks in the music library.
71    pub tracks: Vec<Track>,
72}
73
74/// Subcommand of [`Command`](crate::Command) for managing the music library.
75///
76/// The expected response may be different depending on the command sent. If it isn't specified in
77/// the variant documentation, assume [`Response::Ok`](crate::Response::Ok) is the expected
78/// response.
79#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
80pub enum LibraryCommand {
81    /// Create a new playlist, optionally with some tracks in it.
82    NewPlaylist {
83        /// The name for the new playlist. Must not already exist in the music library.
84        ///
85        /// If a playlist with the specified name already exists,
86        /// [`LibraryError::PlaylistExists`] will be returned, and no changes to the
87        /// [music library](MusicLibrary) will be made.
88        name: String,
89        /// Optional list of paths to tracks to be added to the playlist.
90        ///
91        /// This command will fail if any of the tracks are not registered in the
92        /// [music library](MusicLibrary), or if the list contains duplicates.
93        tracks: Option<Vec<PathBuf>>,
94    },
95    /// Import a playlist from an M3U8 file.
96    ///
97    /// This is currently unimplemented and will cause the `daemon` to panic.
98    PlaylistFromM3U8 {
99        /// The name for the imported playlist. Must not already exist in the music library.
100        ///
101        /// If a playlist with the specified name already exists,
102        /// [`LibraryError::PlaylistExists`] will be returned, and no changes to the
103        /// [music library](MusicLibrary) will be made.
104        name: String,
105        /// The path to the M3U8 file to import.
106        m3u8_file: PathBuf,
107    },
108    /// Delete playlists from the music library.
109    DeletePlaylists {
110        /// List of the playlists to delete.
111        ///
112        /// If any of the provided playlists don't exist in the music library,
113        /// [`LibraryError::NoSuchPlaylist`] will be returned, and no changes to the
114        /// [music library](MusicLibrary) will be made.
115        names: Vec<String>,
116    },
117    /// Add tracks to an already existing playlist.
118    AddTracksToPlaylist {
119        /// The name of the playlist to add tracks to.
120        ///
121        /// If a playlist with the specified name doesn't exist in the music library,
122        /// [`LibraryError::NoSuchPlaylist`] will be returned, and no changes to the
123        /// [music library](MusicLibrary) will be made.
124        name: String,
125        /// List of paths to tracks to add to the playlist.
126        ///
127        /// **Note:** the `daemon` will return an error if any of the tracks are not registered in
128        /// the music library or if the list contains duplicates.
129        tracks: Vec<PathBuf>,
130    },
131    /// Remove tracks from a playlist.
132    RemoveTracksFromPlaylist {
133        /// The name of the playlist to remove tracks from.
134        ///
135        /// If a playlist with the specified name doesn't exist in the music library,
136        /// [`LibraryError::NoSuchPlaylist`] will be returned, and no changes to the
137        /// [music library](MusicLibrary) will be made.
138        name: String,
139        /// The list of track indices in the playlist to remove.
140        ///
141        /// Eg. to remove the first track you would pass `[0]`, to remove the first three
142        /// `[0, 1, 2]`, etc.
143        ///
144        /// If one or more of the indices is out of range, [`LibraryError::IndexOutOfBounds`]
145        /// will be returned, and no changes to the [music library](MusicLibrary) will be made.
146        ids: Vec<usize>,
147    },
148    /// Get the contents of the [music library](MusicLibrary).
149    ///
150    /// The `daemon` will respond to this with [`Response::Library`](crate::Response::Library) if
151    /// successful.
152    GetLibrary,
153    /// Reload the library and rebuild the cache ignoring already cached covers.
154    ///
155    /// Will take more time than just reloading the cache.
156    Rebuild,
157    /// Reload the library using cached data if possible.
158    ///
159    /// This can be used to discover newly added tracks.
160    Reload,
161}
162
163/// An error originating from the music library module of the `daemon`.
164#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
165pub enum LibraryError {
166    /// Could not complete the operation because a [playlist](Playlist) with the provided name
167    /// already exists.
168    PlaylistExists,
169    /// Could not perform the operation because the [music library](MusicLibrary) is not
170    /// initialized.
171    ///
172    /// This can happen if a command is sent to early and the music library is not yet initialized.
173    LibraryNotInitialized,
174    /// There is no [playlist](Playlist) in the [music library](MusicLibrary) with the provided
175    /// name.
176    NoSuchPlaylist,
177    /// The provided item index was out of bounds.
178    IndexOutOfBounds,
179    /// The provided list contained duplicate values.
180    DuplicateItems,
181    /// The provided track is not registered in the library.
182    NoSuchTrack,
183    /// Could not read the contents of the library state file.
184    StateNotReadable,
185    /// Could not write the library state to a file.
186    StateWriteFailed,
187    /// The library state path is not a file.
188    StateNotAFile,
189}
190
191impl std::fmt::Display for LibraryError {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        match self {
194            Self::PlaylistExists => write!(f, "Playlist with this name already exists"),
195            Self::LibraryNotInitialized => write!(f, "The music library is not initialized"),
196            Self::NoSuchPlaylist => write!(f, "There is no playlist with this name"),
197            Self::IndexOutOfBounds => write!(f, "The provided item index was out of bounds"),
198            Self::NoSuchTrack => {
199                write!(f, "The provided track is not registered in the library")
200            }
201            Self::DuplicateItems => write!(f, "The provided vector contained duplicate values"),
202            Self::StateNotReadable => {
203                write!(f, "Could not read the contents of the library state file")
204            }
205            Self::StateWriteFailed => write!(f, "Could not write the library state to a file"),
206            Self::StateNotAFile => write!(f, "The library state path is not a file"),
207        }
208    }
209}