Make music library path changable

This commit is contained in:
Elias Projahn 2020-11-01 10:13:21 +01:00
parent cf96792029
commit 543f5ed1fd
5 changed files with 480 additions and 236 deletions

View file

@ -1,10 +1,16 @@
use super::database::*;
use anyhow::Result;
use futures_channel::oneshot;
use anyhow::{anyhow, Result};
use futures_channel::oneshot::Sender;
use futures_channel::{mpsc, oneshot};
use std::cell::RefCell;
use std::path::PathBuf;
pub enum BackendState {
NoMusicLibrary,
Loading,
Ready,
}
enum BackendAction {
UpdatePerson(Person, Sender<Result<()>>),
GetPerson(i64, Sender<Result<Person>>),
@ -28,24 +34,229 @@ enum BackendAction {
GetRecordingsForPerson(i64, Sender<Result<Vec<RecordingDescription>>>),
GetRecordingsForEnsemble(i64, Sender<Result<Vec<RecordingDescription>>>),
GetRecordingsForWork(i64, Sender<Result<Vec<RecordingDescription>>>),
Stop,
}
use BackendAction::*;
pub struct Backend {
action_sender: std::sync::mpsc::Sender<BackendAction>,
pub state_stream: RefCell<mpsc::Receiver<BackendState>>,
state_sender: RefCell<mpsc::Sender<BackendState>>,
action_sender: RefCell<Option<std::sync::mpsc::Sender<BackendAction>>>,
music_library_path: RefCell<Option<PathBuf>>,
}
impl Backend {
pub fn new(url: &str, music_library_path: PathBuf) -> Self {
let url = url.to_string();
pub fn new() -> Self {
let (state_sender, state_stream) = mpsc::channel(1024);
Backend {
state_stream: RefCell::new(state_stream),
state_sender: RefCell::new(state_sender),
action_sender: RefCell::new(None),
music_library_path: RefCell::new(None),
}
}
pub async fn update_person(&self, person: Person) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(UpdatePerson(person, sender))?;
receiver.await?
}
pub async fn get_person(&self, id: i64) -> Result<Person> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?.send(GetPerson(id, sender))?;
receiver.await?
}
pub async fn delete_person(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(DeletePerson(id, sender))?;
receiver.await?
}
pub async fn get_persons(&self) -> Result<Vec<Person>> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?.send(GetPersons(sender))?;
receiver.await?
}
pub async fn update_instrument(&self, instrument: Instrument) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(UpdateInstrument(instrument, sender))?;
receiver.await?
}
pub async fn get_instrument(&self, id: i64) -> Result<Instrument> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(GetInstrument(id, sender))?;
receiver.await?
}
pub async fn delete_instrument(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(DeleteInstrument(id, sender))?;
receiver.await?
}
pub async fn get_instruments(&self) -> Result<Vec<Instrument>> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?.send(GetInstruments(sender))?;
receiver.await?
}
pub async fn update_work(&self, work_insertion: WorkInsertion) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(UpdateWork(work_insertion, sender))?;
receiver.await?
}
pub async fn get_work_description(&self, id: i64) -> Result<WorkDescription> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(GetWorkDescription(id, sender))?;
receiver.await?
}
pub async fn delete_work(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?.send(DeleteWork(id, sender))?;
receiver.await?
}
pub async fn get_work_descriptions(&self, person_id: i64) -> Result<Vec<WorkDescription>> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(GetWorkDescriptions(person_id, sender))?;
receiver.await?
}
pub async fn update_ensemble(&self, ensemble: Ensemble) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(UpdateEnsemble(ensemble, sender))?;
receiver.await?
}
pub async fn get_ensemble(&self, id: i64) -> Result<Ensemble> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?.send(GetEnsemble(id, sender))?;
receiver.await?
}
pub async fn delete_ensemble(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(DeleteEnsemble(id, sender))?;
receiver.await?
}
pub async fn get_ensembles(&self) -> Result<Vec<Ensemble>> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?.send(GetEnsembles(sender))?;
receiver.await?
}
pub async fn update_recording(&self, recording_insertion: RecordingInsertion) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(UpdateRecording(recording_insertion, sender))?;
receiver.await?
}
pub async fn get_recording_description(&self, id: i64) -> Result<RecordingDescription> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(GetRecordingDescription(id, sender))?;
receiver.await?
}
pub async fn delete_recording(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(DeleteRecording(id, sender))?;
receiver.await?
}
pub async fn get_recordings_for_person(
&self,
person_id: i64,
) -> Result<Vec<RecordingDescription>> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(GetRecordingsForPerson(person_id, sender))?;
receiver.await?
}
pub async fn get_recordings_for_ensemble(
&self,
ensemble_id: i64,
) -> Result<Vec<RecordingDescription>> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(GetRecordingsForEnsemble(ensemble_id, sender))?;
receiver.await?
}
pub async fn get_recordings_for_work(&self, work_id: i64) -> Result<Vec<RecordingDescription>> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(GetRecordingsForWork(work_id, sender))?;
receiver.await?
}
pub async fn set_music_library_path(&self, path: PathBuf) -> Result<()> {
self.music_library_path.replace(Some(path.clone()));
self.set_state(BackendState::Loading);
if let Some(action_sender) = self.action_sender.borrow_mut().take() {
action_sender.send(Stop)?;
}
let mut db_path = path.clone();
db_path.push("musicus.db");
self.start_db_thread(String::from(db_path.to_str().unwrap()))
.await?;
self.set_state(BackendState::Ready);
Ok(())
}
pub fn get_music_library_path(&self) -> Option<PathBuf> {
self.music_library_path.borrow().clone()
}
fn set_state(&self, state: BackendState) {
self.state_sender.borrow_mut().try_send(state).unwrap();
}
fn unwrap_action_sender(&self) -> Result<std::sync::mpsc::Sender<BackendAction>> {
match &*self.action_sender.borrow() {
Some(action_sender) => Ok(action_sender.clone()),
None => Err(anyhow!("Database thread is not running!")),
}
}
async fn start_db_thread(&self, url: String) -> Result<()> {
let (ready_sender, ready_receiver) = oneshot::channel();
let (action_sender, action_receiver) = std::sync::mpsc::channel::<BackendAction>();
std::thread::spawn(move || {
let db = Database::new(&url).expect("Failed to open database!");
ready_sender
.send(())
.expect("Failed to communicate to main thread!");
for action in action_receiver {
match action {
UpdatePerson(person, sender) => {
@ -158,167 +369,15 @@ impl Backend {
.send(db.get_recordings_for_work(id))
.expect("Failed to send result from database thread!");
}
Stop => {
break;
}
}
}
});
Backend {
action_sender: action_sender,
music_library_path: RefCell::new(Some(music_library_path)),
}
}
pub async fn update_person(&self, person: Person) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(UpdatePerson(person, sender))?;
receiver.await?
}
pub async fn get_person(&self, id: i64) -> Result<Person> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetPerson(id, sender))?;
receiver.await?
}
pub async fn delete_person(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeletePerson(id, sender))?;
receiver.await?
}
pub async fn get_persons(&self) -> Result<Vec<Person>> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetPersons(sender))?;
receiver.await?
}
pub async fn update_instrument(&self, instrument: Instrument) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender
.send(UpdateInstrument(instrument, sender))?;
receiver.await?
}
pub async fn get_instrument(&self, id: i64) -> Result<Instrument> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetInstrument(id, sender))?;
receiver.await?
}
pub async fn delete_instrument(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeleteInstrument(id, sender))?;
receiver.await?
}
pub async fn get_instruments(&self) -> Result<Vec<Instrument>> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetInstruments(sender))?;
receiver.await?
}
pub async fn update_work(&self, work_insertion: WorkInsertion) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender
.send(UpdateWork(work_insertion, sender))?;
receiver.await?
}
pub async fn get_work_description(&self, id: i64) -> Result<WorkDescription> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetWorkDescription(id, sender))?;
receiver.await?
}
pub async fn delete_work(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeleteWork(id, sender))?;
receiver.await?
}
pub async fn get_work_descriptions(&self, person_id: i64) -> Result<Vec<WorkDescription>> {
let (sender, receiver) = oneshot::channel();
self.action_sender
.send(GetWorkDescriptions(person_id, sender))?;
receiver.await?
}
pub async fn update_ensemble(&self, ensemble: Ensemble) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(UpdateEnsemble(ensemble, sender))?;
receiver.await?
}
pub async fn get_ensemble(&self, id: i64) -> Result<Ensemble> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetEnsemble(id, sender))?;
receiver.await?
}
pub async fn delete_ensemble(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeleteEnsemble(id, sender))?;
receiver.await?
}
pub async fn get_ensembles(&self) -> Result<Vec<Ensemble>> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetEnsembles(sender))?;
receiver.await?
}
pub async fn update_recording(&self, recording_insertion: RecordingInsertion) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender
.send(UpdateRecording(recording_insertion, sender))?;
receiver.await?
}
pub async fn get_recording_description(&self, id: i64) -> Result<RecordingDescription> {
let (sender, receiver) = oneshot::channel();
self.action_sender
.send(GetRecordingDescription(id, sender))?;
receiver.await?
}
pub async fn delete_recording(&self, id: i64) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeleteRecording(id, sender))?;
receiver.await?
}
pub async fn get_recordings_for_person(
&self,
person_id: i64,
) -> Result<Vec<RecordingDescription>> {
let (sender, receiver) = oneshot::channel();
self.action_sender
.send(GetRecordingsForPerson(person_id, sender))?;
receiver.await?
}
pub async fn get_recordings_for_ensemble(
&self,
ensemble_id: i64,
) -> Result<Vec<RecordingDescription>> {
let (sender, receiver) = oneshot::channel();
self.action_sender
.send(GetRecordingsForEnsemble(ensemble_id, sender))?;
receiver.await?
}
pub async fn get_recordings_for_work(&self, work_id: i64) -> Result<Vec<RecordingDescription>> {
let (sender, receiver) = oneshot::channel();
self.action_sender
.send(GetRecordingsForWork(work_id, sender))?;
receiver.await?
}
pub fn set_music_library_path(&self, path: PathBuf) {
self.music_library_path.replace(Some(path.clone()));
}
pub fn get_music_library_path(&self) -> Option<PathBuf> {
self.music_library_path.borrow().clone()
ready_receiver.await?;
self.action_sender.replace(Some(action_sender));
Ok(())
}
}

View file

@ -29,7 +29,12 @@ impl Preferences {
if let gtk::ResponseType::Accept = dialog.run() {
if let Some(path) = dialog.get_filename() {
music_library_path_row.set_subtitle(Some(path.to_str().unwrap()));
backend.set_music_library_path(path);
let context = glib::MainContext::default();
let backend = backend.clone();
context.spawn_local(async move {
backend.set_music_library_path(path).await.unwrap();
});
}
}
}));

View file

@ -64,8 +64,6 @@ impl PoeList {
result.list.invalidate_filter();
}));
result.clone().reload();
result
}

View file

@ -2,6 +2,7 @@ use crate::backend::*;
use crate::dialogs::*;
use crate::screens::*;
use crate::widgets::*;
use futures::prelude::*;
use gio::prelude::*;
use glib::clone;
use gtk::prelude::*;
@ -12,6 +13,7 @@ use std::rc::Rc;
pub struct Window {
backend: Rc<Backend>,
window: libhandy::ApplicationWindow,
stack: gtk::Stack,
leaflet: libhandy::Leaflet,
sidebar_box: gtk::Box,
poe_list: Rc<PoeList>,
@ -23,47 +25,43 @@ impl Window {
let builder = gtk::Builder::from_resource("/de/johrpan/musicus_editor/ui/window.ui");
get_widget!(builder, libhandy::ApplicationWindow, window);
get_widget!(builder, gtk::Stack, stack);
get_widget!(builder, gtk::Button, select_music_library_path_button);
get_widget!(builder, libhandy::Leaflet, leaflet);
get_widget!(builder, gtk::Button, add_button);
get_widget!(builder, gtk::Box, sidebar_box);
get_widget!(builder, gtk::Box, empty_screen);
let backend = Rc::new(Backend::new(
"test.sqlite",
std::env::current_dir().unwrap(),
));
let backend = Rc::new(Backend::new());
let poe_list = PoeList::new(backend.clone());
let navigator = Navigator::new(&empty_screen);
let result = Rc::new(Self {
backend,
window,
stack,
leaflet,
sidebar_box,
poe_list,
navigator,
});
result
.poe_list
.set_selected(clone!(@strong result => move |poe| {
result.leaflet.set_visible_child(&result.navigator.widget);
match poe {
PersonOrEnsemble::Person(person) => {
result.navigator.clone().replace(PersonScreen::new(result.backend.clone(), person.clone()));
}
PersonOrEnsemble::Ensemble(ensemble) => {
result.navigator.clone().replace(EnsembleScreen::new(result.backend.clone(), ensemble.clone()));
}
}
}));
result.leaflet.add(&result.navigator.widget);
result
.sidebar_box
.pack_start(&result.poe_list.widget, true, true, 0);
result.window.set_application(Some(app));
select_music_library_path_button.connect_clicked(clone!(@strong result => move |_| {
let dialog = gtk::FileChooserNative::new(Some("Select music library folder"), Some(&result.window), gtk::FileChooserAction::SelectFolder, None, None);
if let gtk::ResponseType::Accept = dialog.run() {
if let Some(path) = dialog.get_filename() {
let context = glib::MainContext::default();
let backend = result.backend.clone();
context.spawn_local(async move {
backend.set_music_library_path(path).await.unwrap();
});
}
}
}));
add_button.connect_clicked(clone!(@strong result => move |_| {
TracksEditor::new(result.backend.clone(), &result.window, clone!(@strong result => move || {
result.reload();
@ -266,6 +264,46 @@ impl Window {
})
);
let context = glib::MainContext::default();
let clone = result.clone();
context.spawn_local(async move {
let mut state_stream = clone.backend.state_stream.borrow_mut();
while let Some(state) = state_stream.next().await {
match state {
BackendState::NoMusicLibrary => {
clone.stack.set_visible_child_name("empty");
}
BackendState::Loading => {
clone.stack.set_visible_child_name("loading");
}
BackendState::Ready => {
clone.stack.set_visible_child_name("content");
clone.poe_list.clone().reload();
}
}
}
});
result.leaflet.add(&result.navigator.widget);
result
.poe_list
.set_selected(clone!(@strong result => move |poe| {
result.leaflet.set_visible_child(&result.navigator.widget);
match poe {
PersonOrEnsemble::Person(person) => {
result.navigator.clone().replace(PersonScreen::new(result.backend.clone(), person.clone()));
}
PersonOrEnsemble::Ensemble(ensemble) => {
result.navigator.clone().replace(EnsembleScreen::new(result.backend.clone(), ensemble.clone()));
}
}
}));
result
.sidebar_box
.pack_start(&result.poe_list.widget, true, true, 0);
result
}