| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  | use gio::prelude::*;
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:25:37 +01:00
										 |  |  | use log::warn;
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:56:42 +01:00
										 |  |  | use musicus_client::{Client, LoginData};
 | 
					
						
							|  |  |  | use musicus_database::DbThread;
 | 
					
						
							| 
									
										
										
										
											2021-05-07 20:44:27 +02:00
										 |  |  | use std::cell::{Cell, RefCell};
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  | use std::path::PathBuf;
 | 
					
						
							|  |  |  | use std::rc::Rc;
 | 
					
						
							| 
									
										
										
										
											2021-05-07 20:44:27 +02:00
										 |  |  | use tokio::sync::{broadcast, broadcast::Sender};
 | 
					
						
							| 
									
										
										
										
											2020-11-14 22:32:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:56:42 +01:00
										 |  |  | pub use musicus_client as client;
 | 
					
						
							|  |  |  | pub use musicus_database as db;
 | 
					
						
							| 
									
										
										
										
											2021-02-20 19:03:26 +01:00
										 |  |  | pub use musicus_import as import;
 | 
					
						
							| 
									
										
										
										
											2021-02-03 23:03:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 16:31:37 +01:00
										 |  |  | pub mod error;
 | 
					
						
							|  |  |  | pub use error::*;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  | pub mod library;
 | 
					
						
							|  |  |  | pub use library::*;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-26 00:33:25 +02:00
										 |  |  | mod logger;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-03 23:24:41 +01:00
										 |  |  | pub mod player;
 | 
					
						
							|  |  |  | pub use player::*;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-15 12:38:17 +01:00
										 |  |  | #[cfg(all(feature = "dbus"))]
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  | mod secure;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// General states the application can be in.
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  | #[derive(Debug, Clone)]
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  | pub enum BackendState {
 | 
					
						
							|  |  |  |     /// The backend is not set up yet. This means that no backend methods except for setting the
 | 
					
						
							|  |  |  |     /// music library path should be called. The user interface should adapt and only present this
 | 
					
						
							|  |  |  |     /// option.
 | 
					
						
							|  |  |  |     NoMusicLibrary,
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /// The backend is loading the music library. No methods should be called. The user interface
 | 
					
						
							|  |  |  |     /// should represent that state by prohibiting all interaction.
 | 
					
						
							|  |  |  |     Loading,
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /// The backend is ready and all methods may be called.
 | 
					
						
							|  |  |  |     Ready,
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// A collection of all backend state and functionality.
 | 
					
						
							|  |  |  | pub struct Backend {
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:40:14 +01:00
										 |  |  |     /// The internal sender to publish the state via state_stream.
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |     state_sender: Sender<BackendState>,
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:40:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /// Access to GSettings.
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     settings: gio::Settings,
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:40:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-07 20:44:27 +02:00
										 |  |  |     /// Whether the server should be used by default when searching for or changing items.
 | 
					
						
							|  |  |  |     use_server: Cell<bool>,
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:40:14 +01:00
										 |  |  |     /// The current path to the music library, which is used by the player and the database. This
 | 
					
						
							|  |  |  |     /// is guaranteed to be Some, when the state is set to BackendState::Ready.
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     music_library_path: RefCell<Option<PathBuf>>,
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:40:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-24 18:38:23 +02:00
										 |  |  |     /// The sender for sending library update notifications.
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |     library_updated_sender: Sender<()>,
 | 
					
						
							| 
									
										
										
										
											2021-04-24 18:38:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:40:14 +01:00
										 |  |  |     /// The database. This can be assumed to exist, when the state is set to BackendState::Ready.
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     database: RefCell<Option<Rc<DbThread>>>,
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:40:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /// The player handling playlist and playback. This can be assumed to exist, when the state is
 | 
					
						
							|  |  |  |     /// set to BackendState::Ready.
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     player: RefCell<Option<Rc<Player>>>,
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:40:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /// A client for the Wolfgang server.
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |     client: Client,
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl Backend {
 | 
					
						
							|  |  |  |     /// Create a new backend initerface. The user interface should subscribe to the state stream
 | 
					
						
							| 
									
										
										
										
											2021-04-26 00:33:25 +02:00
										 |  |  |     /// and call init() afterwards. There may be only one backend for a process and this method
 | 
					
						
							|  |  |  |     /// may only be called exactly once. Otherwise it will panic.
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     pub fn new() -> Self {
 | 
					
						
							| 
									
										
										
										
											2021-04-26 00:33:25 +02:00
										 |  |  |         logger::register();
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |         let (state_sender, _) = broadcast::channel(1024);
 | 
					
						
							|  |  |  |         let (library_updated_sender, _) = broadcast::channel(1024);
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Backend {
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |             state_sender,
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |             settings: gio::Settings::new("de.johrpan.musicus"),
 | 
					
						
							| 
									
										
										
										
											2021-05-07 20:44:27 +02:00
										 |  |  |             use_server: Cell::new(true),
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |             music_library_path: RefCell::new(None),
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |             library_updated_sender,
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |             database: RefCell::new(None),
 | 
					
						
							|  |  |  |             player: RefCell::new(None),
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |             client: Client::new(),
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-05 15:50:31 +01:00
										 |  |  |     /// Wait for the next state change. Initially, the state should be assumed to be
 | 
					
						
							|  |  |  |     /// BackendState::Loading. Changes should be awaited before calling init().
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |     pub async fn next_state(&self) -> Result<BackendState> {
 | 
					
						
							|  |  |  |         Ok(self.state_sender.subscribe().recv().await?)
 | 
					
						
							| 
									
										
										
										
											2021-02-05 15:50:31 +01:00
										 |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     /// Initialize the backend updating the state accordingly.
 | 
					
						
							| 
									
										
										
										
											2021-02-05 15:50:31 +01:00
										 |  |  |     pub async fn init(&self) -> Result<()> {
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |         self.init_library().await?;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 20:28:29 +02:00
										 |  |  |         let url = self.settings.string("server-url");
 | 
					
						
							|  |  |  |         if !url.is_empty() {
 | 
					
						
							|  |  |  |             self.client.set_server_url(&url);
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-15 12:38:17 +01:00
										 |  |  |         #[cfg(all(feature = "dbus"))]
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:25:37 +01:00
										 |  |  |         match Self::load_login_data().await {
 | 
					
						
							| 
									
										
										
										
											2021-02-07 00:43:59 +01:00
										 |  |  |             Ok(Some(data)) => self.client.set_login_data(Some(data)),
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |             Err(err) => warn!(
 | 
					
						
							|  |  |  |                 "The login data could not be loaded from SecretService. It will not \
 | 
					
						
							|  |  |  |                 be available. Error message: {}",
 | 
					
						
							|  |  |  |                 err
 | 
					
						
							|  |  |  |             ),
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:25:37 +01:00
										 |  |  |             _ => (),
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-09 18:08:39 +01:00
										 |  |  |         self.use_server.set(self.settings.boolean("use-server"));
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-05 15:50:31 +01:00
										 |  |  |         if self.get_music_library_path().is_none() {
 | 
					
						
							|  |  |  |             self.set_state(BackendState::NoMusicLibrary);
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             self.set_state(BackendState::Ready);
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |         Ok(())
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-07 20:44:27 +02:00
										 |  |  |     /// Whether the server should be used by default.
 | 
					
						
							| 
									
										
										
										
											2022-01-09 18:08:39 +01:00
										 |  |  |     ///
 | 
					
						
							|  |  |  |     /// This will return `false` if no server URL is set up. Otherwise, the
 | 
					
						
							|  |  |  |     /// value is based on the users "use-server" preference.
 | 
					
						
							| 
									
										
										
										
											2021-05-07 20:44:27 +02:00
										 |  |  |     pub fn use_server(&self) -> bool {
 | 
					
						
							| 
									
										
										
										
											2022-01-09 18:08:39 +01:00
										 |  |  |         self.client.get_server_url().is_some() && self.use_server.get()
 | 
					
						
							| 
									
										
										
										
											2021-05-07 20:44:27 +02:00
										 |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /// Set whether the server should be used by default.
 | 
					
						
							|  |  |  |     pub fn set_use_server(&self, enabled: bool) {
 | 
					
						
							|  |  |  |         self.use_server.set(enabled);
 | 
					
						
							| 
									
										
										
										
											2022-01-09 18:08:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if let Err(err) = self.settings.set_boolean("use-server", enabled) {
 | 
					
						
							|  |  |  |             warn!(
 | 
					
						
							|  |  |  |                 "An error happened whilte trying to save the \"use-server\" setting to GSettings. \
 | 
					
						
							|  |  |  |                 Error message: {}",
 | 
					
						
							|  |  |  |                 err
 | 
					
						
							|  |  |  |             )
 | 
					
						
							|  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2021-05-07 20:44:27 +02:00
										 |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |     /// Set the URL of the Musicus server to connect to.
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:25:37 +01:00
										 |  |  |     pub fn set_server_url(&self, url: &str) {
 | 
					
						
							|  |  |  |         if let Err(err) = self.settings.set_string("server-url", url) {
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |             warn!(
 | 
					
						
							|  |  |  |                 "An error happened while trying to save the server URL to GSettings. Most \
 | 
					
						
							|  |  |  |                 likely it will not be available at the next startup. Error message: {}",
 | 
					
						
							|  |  |  |                 err
 | 
					
						
							|  |  |  |             );
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:25:37 +01:00
										 |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |         self.client.set_server_url(url);
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |     /// Get the currently set server URL.
 | 
					
						
							|  |  |  |     pub fn get_server_url(&self) -> Option<String> {
 | 
					
						
							|  |  |  |         self.client.get_server_url()
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /// Set the user credentials to use.
 | 
					
						
							| 
									
										
										
										
											2021-02-07 00:43:59 +01:00
										 |  |  |     pub async fn set_login_data(&self, data: Option<LoginData>) {
 | 
					
						
							| 
									
										
										
										
											2021-02-15 12:38:17 +01:00
										 |  |  |         #[cfg(all(feature = "dbus"))]
 | 
					
						
							| 
									
										
										
										
											2021-02-07 00:43:59 +01:00
										 |  |  |         if let Some(data) = &data {
 | 
					
						
							|  |  |  |             if let Err(err) = Self::store_login_data(data.clone()).await {
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |                 warn!(
 | 
					
						
							|  |  |  |                     "An error happened while trying to store the login data using SecretService. \
 | 
					
						
							| 
									
										
										
										
											2021-02-07 00:43:59 +01:00
										 |  |  |                     This means, that they will not be available at the next startup most likely. \
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |                     Error message: {}",
 | 
					
						
							|  |  |  |                     err
 | 
					
						
							|  |  |  |                 );
 | 
					
						
							| 
									
										
										
										
											2021-02-07 00:43:59 +01:00
										 |  |  |             }
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             if let Err(err) = Self::delete_secrets().await {
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |                 warn!(
 | 
					
						
							|  |  |  |                     "An error happened while trying to delete the login data from SecretService. \
 | 
					
						
							| 
									
										
										
										
											2021-02-07 00:43:59 +01:00
										 |  |  |                     This may result in the login data being reloaded at the next startup. Error \
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |                     message: {}",
 | 
					
						
							|  |  |  |                     err
 | 
					
						
							|  |  |  |                 );
 | 
					
						
							| 
									
										
										
										
											2021-02-07 00:43:59 +01:00
										 |  |  |             }
 | 
					
						
							| 
									
										
										
										
											2021-02-05 10:25:37 +01:00
										 |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 21:47:22 +01:00
										 |  |  |         self.client.set_login_data(data);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn cl(&self) -> &Client {
 | 
					
						
							|  |  |  |         &self.client
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /// Get the currently stored login credentials.
 | 
					
						
							|  |  |  |     pub fn get_login_data(&self) -> Option<LoginData> {
 | 
					
						
							|  |  |  |         self.client.get_login_data()
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     /// Set the current state and notify the user interface.
 | 
					
						
							|  |  |  |     fn set_state(&self, state: BackendState) {
 | 
					
						
							| 
									
										
										
										
											2021-04-25 12:26:43 +02:00
										 |  |  |         self.state_sender.send(state).unwrap();
 | 
					
						
							| 
									
										
										
										
											2020-11-17 15:52:47 +01:00
										 |  |  |     }
 | 
					
						
							|  |  |  | }
 |