musicus/backend/src/lib.rs

183 lines
5.7 KiB
Rust
Raw Normal View History

2021-02-04 21:47:22 +01:00
use gio::prelude::*;
use log::warn;
2021-02-05 10:56:42 +01:00
use musicus_client::{Client, LoginData};
use musicus_database::DbThread;
2020-11-17 15:52:47 +01:00
use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
2021-04-25 12:26:43 +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;
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-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
/// 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
/// and call init() afterwards.
pub fn new() -> Self {
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"),
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-02-04 21:47:22 +01:00
if let Some(url) = self.settings.get_string("server-url") {
if !url.is_empty() {
self.client.set_server_url(&url);
}
}
2021-02-15 12:38:17 +01:00
#[cfg(all(feature = "dbus"))]
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-04 21:47:22 +01:00
}
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(())
}
/// Set the URL of the Musicus server to connect to.
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-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-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
}
}