chilen_backend/music_lib/state/
mod.rs1mod library;
2mod playlist;
3mod track;
4
5use std::{
6 fs::{File, read},
7 io::Write,
8 path::PathBuf,
9 sync::{LazyLock, RwLock},
10 time::{Duration, SystemTime},
11};
12
13use log::{error, trace};
14use rmp_serde::{Deserializer, Serializer};
15use serde::{Deserialize, Serialize};
16
17use crate::{
18 Error, Event,
19 music_lib::{
20 self, DATA_DIR,
21 indexer::{self},
22 },
23};
24
25pub use library::*;
26pub use playlist::*;
27pub use track::*;
28
29#[derive(Debug, Clone, PartialEq)]
30pub enum Progress {
31 FindingTracks,
37
38 Indexing { progress: f32 },
49
50 RebuildingLibrary,
58
59 RestoringState,
65
66 Done,
68
69 Failed(Error),
71}
72
73static LIBRARY_FILE: LazyLock<PathBuf> = LazyLock::new(|| {
74 let mut data = DATA_DIR.read().unwrap().clone().unwrap();
75 data.push("playlists");
76 data
77});
78
79pub(crate) static MUSIC_LIBRARY: RwLock<Option<MusicLibrary>> = RwLock::new(None);
80
81pub(crate) fn unwrap_lib_ref(maybe_lib: Option<&MusicLibrary>) -> Result<&MusicLibrary, Error> {
82 match maybe_lib {
83 Some(lib) => Ok(lib),
84 None => Err(Error::LibraryNotInitialized),
85 }
86}
87
88pub(crate) fn unwrap_lib_mut(
89 maybe_lib: Option<&mut MusicLibrary>,
90) -> Result<&mut MusicLibrary, Error> {
91 match maybe_lib {
92 Some(lib) => Ok(lib),
93 None => Err(Error::LibraryNotInitialized),
94 }
95}
96
97#[cfg(test)]
98pub(crate) fn get_library() -> Result<MusicLibrary, Error> {
99 let guard = MUSIC_LIBRARY.read().unwrap();
100 unwrap_lib_ref(guard.as_ref()).cloned()
101}
102
103pub(crate) fn save_library() -> Result<(), Error> {
105 trace!("Saving the library state");
106
107 let lib = MUSIC_LIBRARY.read().unwrap().clone();
108
109 if let Some(lib_data) = lib {
110 let lib = ConfMusicLibrary::from(lib_data.clone());
111
112 let library_state = LIBRARY_FILE.clone();
113
114 let mut data = Vec::new();
115 lib.serialize(&mut Serializer::new(&mut data)).unwrap();
116
117 let mut file = match File::create(library_state) {
118 Ok(file) => file,
119 Err(e) => {
120 error!("Could not open the library state in write-only mode: {e}");
121 return Err(Error::StateWriteFailed);
122 }
123 };
124
125 match file.write_all(&data) {
126 Ok(_) => Ok(()),
127 Err(e) => {
128 error!("Could not write to the library: {e}");
129 Err(Error::StateWriteFailed)
130 }
131 }
132 } else {
133 error!("Cannot save the library since it is uninitialized!");
134 Err(Error::LibraryNotInitialized)
135 }
136}
137
138pub(crate) fn load(config: music_lib::Config) -> Result<(), Error> {
141 trace!("Loading the music library");
142
143 let time_start = SystemTime::now();
144
145 let tracks = match indexer::index(config.clone()) {
146 Ok(tracks) => tracks,
147 Err(e) => {
148 crate::send_event(Event::LibraryLoadFailed(e.to_string()));
149 return Err(e);
150 }
151 };
152
153 let time_elapsed = time_start.elapsed().unwrap_or(Duration::from_secs(0));
154 trace!(
155 "Finished indexing the music directory in {:.2}s, found {} audio files",
156 time_elapsed.as_secs_f64(),
157 tracks.len()
158 );
159
160 let library_state = LIBRARY_FILE.clone();
161
162 trace!("Loading the library state from {library_state:?}");
163
164 let exists = match library_state.try_exists() {
165 Ok(exists) => exists,
166 Err(e) => {
167 error!("Could not check if the library exists: {e}");
168 crate::send_event(Event::LibraryLoadFailed(e.to_string()));
169 return Err(Error::StateNotReadable);
170 }
171 };
172
173 if exists {
174 trace!("Restoring the library state");
175
176 if !library_state.is_file() {
177 error!("The item at {library_state:?} must be a file!");
178 crate::send_event(Event::LibraryLoadFailed(format!(
179 "The item at {library_state:?} must be a file!"
180 )));
181 return Err(Error::StateNotAFile);
182 }
183
184 let data = match read(library_state) {
185 Ok(data) => data,
186 Err(e) => {
187 error!("Could not read the library: {e}");
188 crate::send_event(Event::LibraryLoadFailed(e.to_string()));
189 return Err(Error::StateNotReadable);
190 }
191 };
192
193 let lib = match ConfMusicLibrary::deserialize(&mut Deserializer::from_read_ref(&data)) {
194 Ok(lib) => {
195 MusicLibrary::load(lib, tracks.clone().into_iter().collect(), config.indexer)
196 }
197 Err(e) => {
198 error!("Could not decode the contents of the library state file: {e}");
199 trace!("Creating a new library");
200 MusicLibrary::new(tracks.into_iter().collect())
201 }
202 };
203
204 *MUSIC_LIBRARY.write().unwrap() = Some(lib);
205 } else {
206 trace!("The library file does not exist, creating a new library");
207 *MUSIC_LIBRARY.write().unwrap() = Some(MusicLibrary::new(tracks));
208 }
209 crate::send_event(Event::LibraryChanged(Box::new(
210 MUSIC_LIBRARY.read().unwrap().as_ref().unwrap().clone(),
211 )));
212 save_library()?;
213
214 let time_elapsed = time_start.elapsed().unwrap_or(Duration::from_secs(0));
215 trace!(
216 "Done loading the music library in {:.2}s",
217 time_elapsed.as_secs_f64()
218 );
219
220 crate::send_event(Event::LoadProgressChanged(Progress::Done));
221
222 Ok(())
223}