2021-02-05 10:56:42 +01:00
|
|
|
use musicus_database::DbThread;
|
2022-01-23 13:18:37 +01:00
|
|
|
use std::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_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::*;
|
|
|
|
|
|
2020-11-17 15:52:47 +01:00
|
|
|
/// 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>>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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"),
|
|
|
|
|
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),
|
2022-01-23 13:18:37 +01:00
|
|
|
player: RefCell::new(None)
|
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-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(())
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|