musicus/src/library.rs

529 lines
19 KiB
Rust
Raw Normal View History

2023-09-30 18:26:11 +02:00
use std::{
2024-03-23 18:06:46 +01:00
cell::{OnceCell, RefCell},
2023-10-07 22:49:20 +02:00
path::{Path, PathBuf},
2023-09-30 18:26:11 +02:00
};
2024-03-23 18:06:46 +01:00
use anyhow::Result;
2024-06-06 15:17:56 +02:00
use chrono::prelude::*;
2024-03-23 18:06:46 +01:00
use diesel::{dsl::exists, prelude::*, QueryDsl, SqliteConnection};
use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*};
2024-06-06 15:17:56 +02:00
use crate::db::{self, models::*, schema::*, tables, TranslatedString};
2024-03-23 18:06:46 +01:00
diesel::sql_function! {
/// Represents the SQL RANDOM() function.
fn random() -> Integer
}
2023-09-30 18:26:11 +02:00
mod imp {
use super::*;
#[derive(Properties, Default)]
#[properties(wrapper_type = super::MusicusLibrary)]
pub struct MusicusLibrary {
2023-10-07 22:49:20 +02:00
#[property(get, construct_only)]
pub folder: OnceCell<String>,
2024-03-23 18:06:46 +01:00
pub connection: RefCell<Option<SqliteConnection>>,
2023-09-30 18:26:11 +02:00
}
#[glib::object_subclass]
impl ObjectSubclass for MusicusLibrary {
const NAME: &'static str = "MusicusLibrary";
type Type = super::MusicusLibrary;
}
#[glib::derived_properties]
2023-10-07 22:49:20 +02:00
impl ObjectImpl for MusicusLibrary {
fn constructed(&self) {
self.parent_constructed();
2024-03-23 18:06:46 +01:00
let db_path = PathBuf::from(&self.folder.get().unwrap()).join("musicus.db");
let connection = db::connect(db_path.to_str().unwrap()).unwrap();
self.connection.set(Some(connection));
2023-10-07 22:49:20 +02:00
}
}
2023-09-30 18:26:11 +02:00
}
glib::wrapper! {
pub struct MusicusLibrary(ObjectSubclass<imp::MusicusLibrary>);
}
impl MusicusLibrary {
pub fn new(path: impl AsRef<Path>) -> Self {
2023-10-07 22:49:20 +02:00
glib::Object::builder()
.property("folder", path.as_ref().to_str().unwrap())
.build()
}
2023-09-30 18:26:11 +02:00
2024-03-23 18:06:46 +01:00
pub fn query(&self, query: &LibraryQuery) -> Result<LibraryResults> {
2023-10-07 22:49:20 +02:00
let search = format!("%{}%", query.search);
2024-03-23 18:06:46 +01:00
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
2023-09-30 18:26:11 +02:00
2024-03-23 18:06:46 +01:00
Ok(match query {
2023-10-07 22:49:20 +02:00
LibraryQuery {
2023-10-08 15:11:47 +02:00
composer: None,
performer: None,
2023-10-07 22:49:20 +02:00
ensemble: None,
work: None,
..
} => {
2024-03-23 18:06:46 +01:00
let composers: Vec<Person> = persons::table
.filter(
exists(
work_persons::table
.filter(work_persons::person_id.eq(persons::person_id)),
)
.and(persons::name.like(&search)),
2023-11-05 14:27:03 +01:00
)
2024-03-23 18:06:46 +01:00
.limit(9)
.load(connection)?;
let performers: Vec<Person> = persons::table
.filter(
exists(
recording_persons::table
.filter(recording_persons::person_id.eq(persons::person_id)),
)
.and(persons::name.like(&search)),
2023-11-05 14:27:03 +01:00
)
2024-03-23 18:06:46 +01:00
.limit(9)
.load(connection)?;
// TODO: Search ensemble persons as well.
let ensembles: Vec<Ensemble> = ensembles::table
.filter(ensembles::name.like(&search))
.limit(9)
.load::<tables::Ensemble>(connection)?
.into_iter()
.map(|e| Ensemble::from_table(e, connection))
.collect::<Result<Vec<Ensemble>>>()?;
let works: Vec<Work> = works::table
.inner_join(work_persons::table.inner_join(persons::table))
.filter(works::name.like(&search).or(persons::name.like(&search)))
.limit(9)
.select(works::all_columns)
.distinct()
.load::<tables::Work>(connection)?
.into_iter()
.map(|w| Work::from_table(w, connection))
.collect::<Result<Vec<Work>>>()?;
2023-10-08 00:16:41 +02:00
2024-06-05 13:26:47 +02:00
let albums: Vec<Album> = albums::table
.filter(albums::name.like(&search))
.limit(9)
.load(connection)?;
2023-10-07 22:49:20 +02:00
LibraryResults {
2023-10-08 15:11:47 +02:00
composers,
performers,
2023-10-08 00:16:41 +02:00
ensembles,
works,
2024-06-05 13:26:47 +02:00
albums,
2023-10-07 22:49:20 +02:00
..Default::default()
}
}
2023-10-08 00:16:41 +02:00
LibraryQuery {
2023-10-08 15:11:47 +02:00
composer: Some(composer),
performer: None,
2023-10-08 00:16:41 +02:00
ensemble: None,
work: None,
..
} => {
2024-03-23 18:06:46 +01:00
let performers: Vec<Person> = persons::table
.inner_join(recording_persons::table.inner_join(
recordings::table.inner_join(works::table.inner_join(work_persons::table)),
))
.filter(
work_persons::person_id
.eq(&composer.person_id)
.and(persons::name.like(&search)),
2023-11-05 14:27:03 +01:00
)
2024-03-23 18:06:46 +01:00
.limit(9)
.select(persons::all_columns)
.distinct()
.load(connection)?;
let ensembles: Vec<Ensemble> = ensembles::table
.inner_join(recording_ensembles::table.inner_join(
recordings::table.inner_join(works::table.inner_join(work_persons::table)),
))
.filter(
work_persons::person_id
.eq(&composer.person_id)
.and(ensembles::name.like(&search)),
2023-11-05 14:27:03 +01:00
)
2024-03-23 18:06:46 +01:00
.limit(9)
.select(ensembles::all_columns)
.distinct()
.load::<tables::Ensemble>(connection)?
.into_iter()
.map(|e| Ensemble::from_table(e, connection))
.collect::<Result<Vec<Ensemble>>>()?;
let works: Vec<Work> = works::table
.inner_join(work_persons::table)
.filter(
work_persons::person_id
.eq(&composer.person_id)
.and(works::name.like(&search)),
)
.limit(9)
.select(works::all_columns)
.distinct()
.load::<tables::Work>(connection)?
.into_iter()
.map(|w| Work::from_table(w, connection))
.collect::<Result<Vec<Work>>>()?;
2023-10-08 00:16:41 +02:00
LibraryResults {
2023-10-08 15:11:47 +02:00
performers,
2023-10-08 00:16:41 +02:00
ensembles,
works,
2023-10-08 15:11:47 +02:00
..Default::default()
2023-10-08 00:16:41 +02:00
}
}
2023-10-08 14:50:27 +02:00
LibraryQuery {
2023-10-08 15:11:47 +02:00
composer: None,
performer: None,
2023-10-08 14:50:27 +02:00
ensemble: Some(ensemble),
work: None,
..
} => {
2024-03-23 18:06:46 +01:00
let composers: Vec<Person> =
persons::table
.inner_join(work_persons::table.inner_join(
works::table.inner_join(
recordings::table.inner_join(recording_ensembles::table),
),
))
.filter(
recording_ensembles::ensemble_id
.eq(&ensemble.ensemble_id)
.and(persons::name.like(&search)),
)
.limit(9)
.select(persons::all_columns)
.distinct()
.load(connection)?;
let recordings = recordings::table
.inner_join(
works::table.inner_join(work_persons::table.inner_join(persons::table)),
2023-11-05 14:27:03 +01:00
)
2024-03-23 18:06:46 +01:00
// .inner_join(recording_persons::table.inner_join(persons::table))
.inner_join(recording_ensembles::table)
.filter(
recording_ensembles::ensemble_id
.eq(&ensemble.ensemble_id)
.and(works::name.like(&search).or(persons::name.like(&search))),
)
.select(recordings::all_columns)
.distinct()
.load::<tables::Recording>(connection)?
.into_iter()
.map(|r| Recording::from_table(r, &&self.folder(), connection))
.collect::<Result<Vec<Recording>>>()?;
2023-10-08 14:50:27 +02:00
2024-06-05 13:26:47 +02:00
let albums = albums::table
.inner_join(
album_recordings::table
.inner_join(recordings::table.inner_join(recording_ensembles::table)),
)
.filter(
recording_ensembles::ensemble_id
.eq(&ensemble.ensemble_id)
.and(albums::name.like(&search)),
)
.select(albums::all_columns)
.distinct()
.load(connection)?;
2023-10-08 14:50:27 +02:00
LibraryResults {
2023-10-08 15:11:47 +02:00
composers,
2023-10-08 14:50:27 +02:00
recordings,
2024-06-05 13:26:47 +02:00
albums,
2023-10-08 14:50:27 +02:00
..Default::default()
}
2023-10-08 16:40:59 +02:00
}
2023-10-08 14:50:27 +02:00
LibraryQuery {
2023-10-08 15:11:47 +02:00
composer: None,
performer: Some(performer),
work: None,
..
} => {
2024-03-23 18:06:46 +01:00
let composers: Vec<Person> = persons::table
.inner_join(
work_persons::table
.inner_join(works::table.inner_join(
recordings::table.inner_join(recording_persons::table),
)),
2023-11-05 14:27:03 +01:00
)
2024-03-23 18:06:46 +01:00
.filter(
recording_persons::person_id
.eq(&performer.person_id)
.and(persons::name.like(&search)),
)
.limit(9)
.select(persons::all_columns)
.distinct()
.load(connection)?;
let recordings = recordings::table
.inner_join(
works::table.inner_join(work_persons::table.inner_join(persons::table)),
)
.inner_join(recording_persons::table)
.filter(
recording_persons::person_id
.eq(&performer.person_id)
.and(works::name.like(&search).or(persons::name.like(&search))),
)
.select(recordings::all_columns)
.distinct()
.load::<tables::Recording>(connection)?
.into_iter()
.map(|r| Recording::from_table(r, &self.folder(), connection))
.collect::<Result<Vec<Recording>>>()?;
2023-10-08 15:11:47 +02:00
2024-06-05 13:26:47 +02:00
let albums = albums::table
.inner_join(
album_recordings::table
.inner_join(recordings::table.inner_join(recording_persons::table)),
)
.filter(
recording_persons::person_id
.eq(&performer.person_id)
.and(albums::name.like(&search)),
)
.select(albums::all_columns)
.distinct()
.load(connection)?;
2023-10-08 15:11:47 +02:00
LibraryResults {
composers,
recordings,
2024-06-05 13:26:47 +02:00
albums,
2023-10-08 15:11:47 +02:00
..Default::default()
}
2023-10-08 16:40:59 +02:00
}
2023-10-08 15:11:47 +02:00
LibraryQuery {
composer: Some(composer),
2023-10-08 14:50:27 +02:00
ensemble: Some(ensemble),
work: None,
..
} => {
2024-03-23 18:06:46 +01:00
let recordings = recordings::table
.inner_join(works::table.inner_join(work_persons::table))
.inner_join(recording_ensembles::table)
.filter(
work_persons::person_id
.eq(&composer.person_id)
.and(recording_ensembles::ensemble_id.eq(&ensemble.ensemble_id))
.and(works::name.like(search)),
)
.select(recordings::all_columns)
.distinct()
.load::<tables::Recording>(connection)?
.into_iter()
.map(|r| Recording::from_table(r, &self.folder(), connection))
.collect::<Result<Vec<Recording>>>()?;
2023-10-08 15:11:47 +02:00
LibraryResults {
recordings,
..Default::default()
}
2023-10-08 16:40:59 +02:00
}
2023-10-08 15:11:47 +02:00
LibraryQuery {
composer: Some(composer),
performer: Some(performer),
work: None,
..
} => {
2024-03-23 18:06:46 +01:00
let recordings = recordings::table
.inner_join(works::table.inner_join(work_persons::table))
.inner_join(recording_persons::table)
.filter(
work_persons::person_id
.eq(&composer.person_id)
.and(recording_persons::person_id.eq(&performer.person_id))
.and(works::name.like(search)),
)
.select(recordings::all_columns)
.distinct()
.load::<tables::Recording>(connection)?
.into_iter()
.map(|r| Recording::from_table(r, &self.folder(), connection))
.collect::<Result<Vec<Recording>>>()?;
2023-10-08 14:50:27 +02:00
LibraryResults {
recordings,
..Default::default()
}
2023-10-08 16:40:59 +02:00
}
2023-10-08 14:50:27 +02:00
LibraryQuery {
2023-10-08 16:40:59 +02:00
work: Some(work), ..
2023-10-08 14:50:27 +02:00
} => {
2024-03-23 18:06:46 +01:00
let recordings = recordings::table
.filter(recordings::work_id.eq(&work.work_id))
.load::<tables::Recording>(connection)?
.into_iter()
.map(|r| Recording::from_table(r, &self.folder(), connection))
.collect::<Result<Vec<Recording>>>()?;
2023-10-08 14:50:27 +02:00
LibraryResults {
recordings,
..Default::default()
}
}
2024-03-23 18:06:46 +01:00
})
2023-10-25 17:45:32 +02:00
}
2024-03-23 18:06:46 +01:00
pub fn random_recording(&self, query: &LibraryQuery) -> Result<Recording> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
2023-10-25 17:45:32 +02:00
match query {
2024-03-23 18:06:46 +01:00
LibraryQuery { .. } => Recording::from_table(
recordings::table
.order(random())
.first::<tables::Recording>(connection)?,
&self.folder(),
connection,
),
2023-10-25 17:45:32 +02:00
}
}
2024-05-31 13:39:27 +02:00
pub fn search_persons(&self, search: &str) -> Result<Vec<Person>> {
let search = format!("%{}%", search);
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let persons = persons::table
.order(persons::last_used_at.desc())
.filter(persons::name.like(&search))
.limit(20)
.load(connection)?;
Ok(persons)
}
pub fn search_roles(&self, search: &str) -> Result<Vec<Role>> {
let search = format!("%{}%", search);
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let roles = roles::table
.order(roles::last_used_at.desc())
.filter(roles::name.like(&search))
.limit(20)
.load(connection)?;
Ok(roles)
}
pub fn search_instruments(&self, search: &str) -> Result<Vec<Instrument>> {
let search = format!("%{}%", search);
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let instruments = instruments::table
.order(instruments::last_used_at.desc())
.filter(instruments::name.like(&search))
.limit(20)
.load(connection)?;
Ok(instruments)
}
pub fn composer_default_role(&self) -> Result<Role> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
Ok(roles::table
.filter(roles::role_id.eq("380d7e09eb2f49c1a90db2ba4acb6ffd"))
.first::<Role>(connection)?)
}
2024-06-06 15:17:56 +02:00
pub fn create_person(&self, name: TranslatedString) -> Result<Person> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let now = Local::now().naive_local();
let person = Person {
person_id: db::generate_id(),
name,
created_at: now,
edited_at: now,
last_used_at: now,
last_played_at: None,
};
diesel::insert_into(persons::table)
.values(&person)
.execute(connection)?;
Ok(person)
}
pub fn update_person(&self, id: &str, name: TranslatedString) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let now = Local::now().naive_local();
diesel::update(persons::table)
.filter(persons::person_id.eq(id))
.set((
persons::name.eq(name),
persons::edited_at.eq(now),
persons::last_used_at.eq(now),
))
.execute(connection)?;
Ok(())
}
2023-09-30 18:26:11 +02:00
}
2023-10-07 22:49:20 +02:00
2023-10-08 00:16:41 +02:00
#[derive(Default, Debug)]
2023-10-07 22:49:20 +02:00
pub struct LibraryQuery {
2023-10-08 15:11:47 +02:00
pub composer: Option<Person>,
pub performer: Option<Person>,
2023-10-07 22:49:20 +02:00
pub ensemble: Option<Ensemble>,
pub work: Option<Work>,
pub search: String,
}
2024-04-01 18:43:00 +02:00
impl LibraryQuery {
pub fn is_empty(&self) -> bool {
self.composer.is_none()
&& self.performer.is_none()
&& self.ensemble.is_none()
&& self.work.is_none()
&& self.search.is_empty()
}
}
2023-10-08 00:16:41 +02:00
#[derive(Default, Debug)]
2023-10-07 22:49:20 +02:00
pub struct LibraryResults {
2023-10-08 15:11:47 +02:00
pub composers: Vec<Person>,
pub performers: Vec<Person>,
2023-10-07 22:49:20 +02:00
pub ensembles: Vec<Ensemble>,
pub works: Vec<Work>,
pub recordings: Vec<Recording>,
2024-06-05 13:26:47 +02:00
pub albums: Vec<Album>,
2023-10-07 22:49:20 +02:00
}
impl LibraryResults {
pub fn is_empty(&self) -> bool {
2023-10-08 15:11:47 +02:00
self.composers.is_empty()
&& self.performers.is_empty()
2023-10-08 00:16:41 +02:00
&& self.ensembles.is_empty()
&& self.works.is_empty()
&& self.recordings.is_empty()
2024-06-05 13:26:47 +02:00
&& self.albums.is_empty()
2023-10-07 22:49:20 +02:00
}
}