mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 19:57:25 +01:00
database: Store access times
This commit is contained in:
parent
5c64bdef7e
commit
a0554a478f
20 changed files with 315 additions and 104 deletions
31
Cargo.lock
generated
31
Cargo.lock
generated
|
|
@ -121,6 +121,19 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"time",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
@ -353,7 +366,7 @@ checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1016,6 +1029,7 @@ dependencies = [
|
||||||
name = "musicus_database"
|
name = "musicus_database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
"log",
|
"log",
|
||||||
|
|
@ -1481,6 +1495,17 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
|
|
@ -1564,9 +1589,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
diesel = { version = "1.4.5", features = ["sqlite"] }
|
diesel = { version = "1.4.5", features = ["sqlite"] }
|
||||||
diesel_migrations = "1.4.0"
|
diesel_migrations = "1.4.0"
|
||||||
|
chrono = "0.4.19"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
thiserror = "1.0.23"
|
thiserror = "1.0.23"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
ALTER TABLE "persons" DROP COLUMN "last_used";
|
||||||
|
ALTER TABLE "persons" DROP COLUMN "last_played";
|
||||||
|
|
||||||
|
ALTER TABLE "instruments" DROP COLUMN "last_used";
|
||||||
|
ALTER TABLE "instruments" DROP COLUMN "last_played";
|
||||||
|
|
||||||
|
ALTER TABLE "works" DROP COLUMN "last_used";
|
||||||
|
ALTER TABLE "works" DROP COLUMN "last_played";
|
||||||
|
|
||||||
|
ALTER TABLE "ensembles" DROP COLUMN "last_used";
|
||||||
|
ALTER TABLE "ensembles" DROP COLUMN "last_played";
|
||||||
|
|
||||||
|
ALTER TABLE "recordings" DROP COLUMN "last_used";
|
||||||
|
ALTER TABLE "recordings" DROP COLUMN "last_played";
|
||||||
|
|
||||||
|
ALTER TABLE "mediums" DROP COLUMN "last_used";
|
||||||
|
ALTER TABLE "mediums" DROP COLUMN "last_played";
|
||||||
|
|
||||||
|
ALTER TABLE "tracks" DROP COLUMN "last_used";
|
||||||
|
ALTER TABLE "tracks" DROP COLUMN "last_played";
|
||||||
21
database/migrations/2022-04-10-103835_access_history/up.sql
Normal file
21
database/migrations/2022-04-10-103835_access_history/up.sql
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
ALTER TABLE "persons" ADD COLUMN "last_used" BIGINT;
|
||||||
|
ALTER TABLE "persons" ADD COLUMN "last_played" BIGINT;
|
||||||
|
|
||||||
|
ALTER TABLE "instruments" ADD COLUMN "last_used" BIGINT;
|
||||||
|
ALTER TABLE "instruments" ADD COLUMN "last_played" BIGINT;
|
||||||
|
|
||||||
|
ALTER TABLE "works" ADD COLUMN "last_used" BIGINT;
|
||||||
|
ALTER TABLE "works" ADD COLUMN "last_played" BIGINT;
|
||||||
|
|
||||||
|
ALTER TABLE "ensembles" ADD COLUMN "last_used" BIGINT;
|
||||||
|
ALTER TABLE "ensembles" ADD COLUMN "last_played" BIGINT;
|
||||||
|
|
||||||
|
ALTER TABLE "recordings" ADD COLUMN "last_used" BIGINT;
|
||||||
|
ALTER TABLE "recordings" ADD COLUMN "last_played" BIGINT;
|
||||||
|
|
||||||
|
ALTER TABLE "mediums" ADD COLUMN "last_used" BIGINT;
|
||||||
|
ALTER TABLE "mediums" ADD COLUMN "last_played" BIGINT;
|
||||||
|
|
||||||
|
ALTER TABLE "tracks" ADD COLUMN "last_used" BIGINT;
|
||||||
|
ALTER TABLE "tracks" ADD COLUMN "last_played" BIGINT;
|
||||||
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use super::schema::ensembles;
|
use super::schema::ensembles;
|
||||||
use super::{Database, Result};
|
use super::{Database, Result};
|
||||||
|
use chrono::Utc;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
|
@ -8,14 +9,29 @@ use log::info;
|
||||||
pub struct Ensemble {
|
pub struct Ensemble {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub last_used: Option<i64>,
|
||||||
|
pub last_played: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ensemble {
|
||||||
|
pub fn new(id: String, name: String) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
last_used: Some(Utc::now().timestamp()),
|
||||||
|
last_played: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
/// Update an existing ensemble or insert a new one.
|
/// Update an existing ensemble or insert a new one.
|
||||||
pub fn update_ensemble(&self, ensemble: Ensemble) -> Result<()> {
|
pub fn update_ensemble(&self, mut ensemble: Ensemble) -> Result<()> {
|
||||||
info!("Updating ensemble {:?}", ensemble);
|
info!("Updating ensemble {:?}", ensemble);
|
||||||
self.defer_foreign_keys()?;
|
self.defer_foreign_keys()?;
|
||||||
|
|
||||||
|
ensemble.last_used = Some(Utc::now().timestamp());
|
||||||
|
|
||||||
self.connection.transaction(|| {
|
self.connection.transaction(|| {
|
||||||
diesel::replace_into(ensembles::table)
|
diesel::replace_into(ensembles::table)
|
||||||
.values(ensemble)
|
.values(ensemble)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use super::schema::instruments;
|
use super::schema::instruments;
|
||||||
use super::{Database, Result};
|
use super::{Database, Result};
|
||||||
|
use chrono::Utc;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
|
@ -8,14 +9,29 @@ use log::info;
|
||||||
pub struct Instrument {
|
pub struct Instrument {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub last_used: Option<i64>,
|
||||||
|
pub last_played: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instrument {
|
||||||
|
pub fn new(id: String, name: String) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
last_used: Some(Utc::now().timestamp()),
|
||||||
|
last_played: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
/// Update an existing instrument or insert a new one.
|
/// Update an existing instrument or insert a new one.
|
||||||
pub fn update_instrument(&self, instrument: Instrument) -> Result<()> {
|
pub fn update_instrument(&self, mut instrument: Instrument) -> Result<()> {
|
||||||
info!("Updating instrument {:?}", instrument);
|
info!("Updating instrument {:?}", instrument);
|
||||||
self.defer_foreign_keys()?;
|
self.defer_foreign_keys()?;
|
||||||
|
|
||||||
|
instrument.last_used = Some(Utc::now().timestamp());
|
||||||
|
|
||||||
self.connection.transaction(|| {
|
self.connection.transaction(|| {
|
||||||
diesel::replace_into(instruments::table)
|
diesel::replace_into(instruments::table)
|
||||||
.values(instrument)
|
.values(instrument)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::generate_id;
|
use super::generate_id;
|
||||||
use super::schema::{ensembles, mediums, performances, persons, recordings, tracks};
|
use super::schema::{ensembles, mediums, performances, persons, recordings, tracks};
|
||||||
use super::{Database, Error, Recording, Result};
|
use super::{Database, Error, Recording, Result};
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
|
@ -19,6 +20,22 @@ pub struct Medium {
|
||||||
|
|
||||||
/// The tracks of the medium.
|
/// The tracks of the medium.
|
||||||
pub tracks: Vec<Track>,
|
pub tracks: Vec<Track>,
|
||||||
|
|
||||||
|
pub last_used: Option<DateTime<Utc>>,
|
||||||
|
pub last_played: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Medium {
|
||||||
|
pub fn new(id: String, name: String, discid: Option<String>, tracks: Vec<Track>) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
discid,
|
||||||
|
tracks,
|
||||||
|
last_used: Some(Utc::now()),
|
||||||
|
last_played: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A track on a medium.
|
/// A track on a medium.
|
||||||
|
|
@ -37,6 +54,22 @@ pub struct Track {
|
||||||
|
|
||||||
/// The path to the audio file containing this track.
|
/// The path to the audio file containing this track.
|
||||||
pub path: String,
|
pub path: String,
|
||||||
|
|
||||||
|
pub last_used: Option<DateTime<Utc>>,
|
||||||
|
pub last_played: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Track {
|
||||||
|
pub fn new(recording: Recording, work_parts: Vec<usize>, source_index: usize, path: String) -> Self {
|
||||||
|
Self {
|
||||||
|
recording,
|
||||||
|
work_parts,
|
||||||
|
source_index,
|
||||||
|
path,
|
||||||
|
last_used: Some(Utc::now()),
|
||||||
|
last_played: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Table data for a [`Medium`].
|
/// Table data for a [`Medium`].
|
||||||
|
|
@ -46,6 +79,8 @@ struct MediumRow {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub discid: Option<String>,
|
pub discid: Option<String>,
|
||||||
|
pub last_used: Option<i64>,
|
||||||
|
pub last_played: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Table data for a [`Track`].
|
/// Table data for a [`Track`].
|
||||||
|
|
@ -59,6 +94,8 @@ struct TrackRow {
|
||||||
pub work_parts: String,
|
pub work_parts: String,
|
||||||
pub source_index: i32,
|
pub source_index: i32,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
|
pub last_used: Option<i64>,
|
||||||
|
pub last_played: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
|
|
@ -79,6 +116,8 @@ impl Database {
|
||||||
id: medium_id.to_owned(),
|
id: medium_id.to_owned(),
|
||||||
name: medium.name.clone(),
|
name: medium.name.clone(),
|
||||||
discid: medium.discid.clone(),
|
discid: medium.discid.clone(),
|
||||||
|
last_used: Some(Utc::now().timestamp()),
|
||||||
|
last_played: medium.last_played.map(|t| t.timestamp()),
|
||||||
};
|
};
|
||||||
|
|
||||||
diesel::insert_into(mediums::table)
|
diesel::insert_into(mediums::table)
|
||||||
|
|
@ -109,6 +148,8 @@ impl Database {
|
||||||
work_parts,
|
work_parts,
|
||||||
source_index: track.source_index as i32,
|
source_index: track.source_index as i32,
|
||||||
path: track.path.clone(),
|
path: track.path.clone(),
|
||||||
|
last_used: Some(Utc::now().timestamp()),
|
||||||
|
last_played: track.last_played.map(|t| t.timestamp()),
|
||||||
};
|
};
|
||||||
|
|
||||||
diesel::insert_into(tracks::table)
|
diesel::insert_into(tracks::table)
|
||||||
|
|
@ -254,6 +295,8 @@ impl Database {
|
||||||
name: row.name,
|
name: row.name,
|
||||||
discid: row.discid,
|
discid: row.discid,
|
||||||
tracks,
|
tracks,
|
||||||
|
last_used: row.last_used.map(|t| Utc.timestamp(t, 0)),
|
||||||
|
last_played: row.last_played.map(|t| Utc.timestamp(t, 0)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(medium)
|
Ok(medium)
|
||||||
|
|
@ -285,6 +328,8 @@ impl Database {
|
||||||
work_parts: part_indices,
|
work_parts: part_indices,
|
||||||
source_index: row.source_index as usize,
|
source_index: row.source_index as usize,
|
||||||
path: row.path,
|
path: row.path,
|
||||||
|
last_used: row.last_used.map(|t| Utc.timestamp(t, 0)),
|
||||||
|
last_played: row.last_played.map(|t| Utc.timestamp(t, 0)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(track)
|
Ok(track)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use super::schema::persons;
|
use super::schema::persons;
|
||||||
use super::{Database, Result};
|
use super::{Database, Result};
|
||||||
|
use chrono::Utc;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
|
@ -9,9 +10,21 @@ pub struct Person {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub first_name: String,
|
pub first_name: String,
|
||||||
pub last_name: String,
|
pub last_name: String,
|
||||||
|
pub last_used: Option<i64>,
|
||||||
|
pub last_played: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Person {
|
impl Person {
|
||||||
|
pub fn new(id: String, first_name: String, last_name: String) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
last_used: Some(Utc::now().timestamp()),
|
||||||
|
last_played: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the full name in the form "First Last".
|
/// Get the full name in the form "First Last".
|
||||||
pub fn name_fl(&self) -> String {
|
pub fn name_fl(&self) -> String {
|
||||||
format!("{} {}", self.first_name, self.last_name)
|
format!("{} {}", self.first_name, self.last_name)
|
||||||
|
|
@ -25,10 +38,12 @@ impl Person {
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
/// Update an existing person or insert a new one.
|
/// Update an existing person or insert a new one.
|
||||||
pub fn update_person(&self, person: Person) -> Result<()> {
|
pub fn update_person(&self, mut person: Person) -> Result<()> {
|
||||||
info!("Updating person {:?}", person);
|
info!("Updating person {:?}", person);
|
||||||
self.defer_foreign_keys()?;
|
self.defer_foreign_keys()?;
|
||||||
|
|
||||||
|
person.last_used = Some(Utc::now().timestamp());
|
||||||
|
|
||||||
self.connection.transaction(|| {
|
self.connection.transaction(|| {
|
||||||
diesel::replace_into(persons::table)
|
diesel::replace_into(persons::table)
|
||||||
.values(person)
|
.values(person)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::generate_id;
|
use super::generate_id;
|
||||||
use super::schema::{ensembles, performances, persons, recordings};
|
use super::schema::{ensembles, performances, persons, recordings};
|
||||||
use super::{Database, Ensemble, Error, Instrument, Person, Result, Work};
|
use super::{Database, Ensemble, Error, Instrument, Person, Result, Work};
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
|
@ -11,16 +12,31 @@ pub struct Recording {
|
||||||
pub work: Work,
|
pub work: Work,
|
||||||
pub comment: String,
|
pub comment: String,
|
||||||
pub performances: Vec<Performance>,
|
pub performances: Vec<Performance>,
|
||||||
|
pub last_used: Option<DateTime<Utc>>,
|
||||||
|
pub last_played: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Recording {
|
impl Recording {
|
||||||
|
pub fn new(id: String, work: Work, comment: String, performances: Vec<Performance>) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
work,
|
||||||
|
comment,
|
||||||
|
performances,
|
||||||
|
last_used: Some(Utc::now()),
|
||||||
|
last_played: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize a new recording with a work.
|
/// Initialize a new recording with a work.
|
||||||
pub fn new(work: Work) -> Self {
|
pub fn from_work(work: Work) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: generate_id(),
|
id: generate_id(),
|
||||||
work,
|
work,
|
||||||
comment: String::new(),
|
comment: String::new(),
|
||||||
performances: Vec::new(),
|
performances: Vec::new(),
|
||||||
|
last_used: Some(Utc::now()),
|
||||||
|
last_played: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,6 +98,8 @@ struct RecordingRow {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub work: String,
|
pub work: String,
|
||||||
pub comment: String,
|
pub comment: String,
|
||||||
|
pub last_used: Option<i64>,
|
||||||
|
pub last_played: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Recording> for RecordingRow {
|
impl From<Recording> for RecordingRow {
|
||||||
|
|
@ -90,6 +108,8 @@ impl From<Recording> for RecordingRow {
|
||||||
id: recording.id,
|
id: recording.id,
|
||||||
work: recording.work.id,
|
work: recording.work.id,
|
||||||
comment: recording.comment,
|
comment: recording.comment,
|
||||||
|
last_used: Some(Utc::now().timestamp()),
|
||||||
|
last_played: recording.last_played.map(|t| t.timestamp()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -255,6 +275,8 @@ impl Database {
|
||||||
work,
|
work,
|
||||||
comment: row.comment,
|
comment: row.comment,
|
||||||
performances: performance_descriptions,
|
performances: performance_descriptions,
|
||||||
|
last_used: row.last_used.map(|t| Utc.timestamp(t, 0)),
|
||||||
|
last_played: row.last_played.map(|t| Utc.timestamp(t, 0)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(recording_description)
|
Ok(recording_description)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ table! {
|
||||||
ensembles (id) {
|
ensembles (id) {
|
||||||
id -> Text,
|
id -> Text,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
last_used -> Nullable<BigInt>,
|
||||||
|
last_played -> Nullable<BigInt>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,6 +19,8 @@ table! {
|
||||||
instruments (id) {
|
instruments (id) {
|
||||||
id -> Text,
|
id -> Text,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
last_used -> Nullable<BigInt>,
|
||||||
|
last_played -> Nullable<BigInt>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,6 +29,8 @@ table! {
|
||||||
id -> Text,
|
id -> Text,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
discid -> Nullable<Text>,
|
discid -> Nullable<Text>,
|
||||||
|
last_used -> Nullable<BigInt>,
|
||||||
|
last_played -> Nullable<BigInt>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,6 +49,8 @@ table! {
|
||||||
id -> Text,
|
id -> Text,
|
||||||
first_name -> Text,
|
first_name -> Text,
|
||||||
last_name -> Text,
|
last_name -> Text,
|
||||||
|
last_used -> Nullable<BigInt>,
|
||||||
|
last_played -> Nullable<BigInt>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,6 +59,8 @@ table! {
|
||||||
id -> Text,
|
id -> Text,
|
||||||
work -> Text,
|
work -> Text,
|
||||||
comment -> Text,
|
comment -> Text,
|
||||||
|
last_used -> Nullable<BigInt>,
|
||||||
|
last_played -> Nullable<BigInt>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,6 +73,8 @@ table! {
|
||||||
work_parts -> Text,
|
work_parts -> Text,
|
||||||
source_index -> Integer,
|
source_index -> Integer,
|
||||||
path -> Text,
|
path -> Text,
|
||||||
|
last_used -> Nullable<BigInt>,
|
||||||
|
last_played -> Nullable<BigInt>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +92,8 @@ table! {
|
||||||
id -> Text,
|
id -> Text,
|
||||||
composer -> Text,
|
composer -> Text,
|
||||||
title -> Text,
|
title -> Text,
|
||||||
|
last_used -> Nullable<BigInt>,
|
||||||
|
last_played -> Nullable<BigInt>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::generate_id;
|
use super::generate_id;
|
||||||
use super::schema::{instrumentations, work_parts, works};
|
use super::schema::{instrumentations, work_parts, works};
|
||||||
use super::{Database, Error, Instrument, Person, Result};
|
use super::{Database, Error, Instrument, Person, Result};
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::{Insertable, Queryable};
|
use diesel::{Insertable, Queryable};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
@ -12,6 +13,8 @@ struct WorkRow {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub composer: String,
|
pub composer: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
pub last_used: Option<i64>,
|
||||||
|
pub last_played: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Work> for WorkRow {
|
impl From<Work> for WorkRow {
|
||||||
|
|
@ -20,6 +23,8 @@ impl From<Work> for WorkRow {
|
||||||
id: work.id,
|
id: work.id,
|
||||||
composer: work.composer.id,
|
composer: work.composer.id,
|
||||||
title: work.title,
|
title: work.title,
|
||||||
|
last_used: Some(Utc::now().timestamp()),
|
||||||
|
last_played: work.last_played.map(|t| t.timestamp()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,17 +62,33 @@ pub struct Work {
|
||||||
pub composer: Person,
|
pub composer: Person,
|
||||||
pub instruments: Vec<Instrument>,
|
pub instruments: Vec<Instrument>,
|
||||||
pub parts: Vec<WorkPart>,
|
pub parts: Vec<WorkPart>,
|
||||||
|
pub last_used: Option<DateTime<Utc>>,
|
||||||
|
pub last_played: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Work {
|
impl Work {
|
||||||
|
pub fn new(id: String, title: String, composer: Person, instruments: Vec<Instrument>, parts: Vec<WorkPart>) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
composer,
|
||||||
|
instruments,
|
||||||
|
parts,
|
||||||
|
last_used: Some(Utc::now()),
|
||||||
|
last_played: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize a new work with a composer.
|
/// Initialize a new work with a composer.
|
||||||
pub fn new(composer: Person) -> Self {
|
pub fn from_composer(composer: Person) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: generate_id(),
|
id: generate_id(),
|
||||||
title: String::new(),
|
title: String::new(),
|
||||||
composer,
|
composer,
|
||||||
instruments: Vec::new(),
|
instruments: Vec::new(),
|
||||||
parts: Vec::new(),
|
parts: Vec::new(),
|
||||||
|
last_used: Some(Utc::now()),
|
||||||
|
last_played: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,9 +130,7 @@ impl Database {
|
||||||
.execute(&self.connection)?;
|
.execute(&self.connection)?;
|
||||||
|
|
||||||
let Work {
|
let Work {
|
||||||
instruments,
|
instruments, parts, ..
|
||||||
parts,
|
|
||||||
..
|
|
||||||
} = work;
|
} = work;
|
||||||
|
|
||||||
for instrument in instruments {
|
for instrument in instruments {
|
||||||
|
|
@ -200,6 +219,8 @@ impl Database {
|
||||||
title: row.title,
|
title: row.title,
|
||||||
instruments,
|
instruments,
|
||||||
parts,
|
parts,
|
||||||
|
last_used: row.last_used.map(|t| Utc.timestamp(t, 0)),
|
||||||
|
last_played: row.last_played.map(|t| Utc.timestamp(t, 0)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,10 +88,7 @@ impl EnsembleEditor {
|
||||||
fn save(&self) -> Result<Ensemble> {
|
fn save(&self) -> Result<Ensemble> {
|
||||||
let name = self.name.get_text();
|
let name = self.name.get_text();
|
||||||
|
|
||||||
let ensemble = Ensemble {
|
let ensemble = Ensemble::new(self.id.clone(), name);
|
||||||
id: self.id.clone(),
|
|
||||||
name,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.handle.backend.db().update_ensemble(ensemble.clone())?;
|
self.handle.backend.db().update_ensemble(ensemble.clone())?;
|
||||||
self.handle.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
|
||||||
|
|
@ -88,12 +88,12 @@ impl InstrumentEditor {
|
||||||
fn save(&self) -> Result<Instrument> {
|
fn save(&self) -> Result<Instrument> {
|
||||||
let name = self.name.get_text();
|
let name = self.name.get_text();
|
||||||
|
|
||||||
let instrument = Instrument {
|
let instrument = Instrument::new(self.id.clone(), name);
|
||||||
id: self.id.clone(),
|
|
||||||
name,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.handle.backend.db().update_instrument(instrument.clone())?;
|
self.handle
|
||||||
|
.backend
|
||||||
|
.db()
|
||||||
|
.update_instrument(instrument.clone())?;
|
||||||
self.handle.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
||||||
Ok(instrument)
|
Ok(instrument)
|
||||||
|
|
|
||||||
|
|
@ -102,11 +102,7 @@ impl PersonEditor {
|
||||||
let first_name = self.first_name.get_text();
|
let first_name = self.first_name.get_text();
|
||||||
let last_name = self.last_name.get_text();
|
let last_name = self.last_name.get_text();
|
||||||
|
|
||||||
let person = Person {
|
let person = Person::new(self.id.clone(), first_name, last_name);
|
||||||
id: self.id.clone(),
|
|
||||||
first_name,
|
|
||||||
last_name,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.handle.backend.db().update_person(person.clone())?;
|
self.handle.backend.db().update_person(person.clone())?;
|
||||||
self.handle.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
|
||||||
|
|
@ -177,18 +177,20 @@ impl RecordingEditor {
|
||||||
|
|
||||||
/// Save the recording.
|
/// Save the recording.
|
||||||
fn save(self: &Rc<Self>) -> Result<Recording> {
|
fn save(self: &Rc<Self>) -> Result<Recording> {
|
||||||
let recording = Recording {
|
let recording = Recording::new(
|
||||||
id: self.id.clone(),
|
self.id.clone(),
|
||||||
work: self
|
self.work
|
||||||
.work
|
|
||||||
.borrow()
|
.borrow()
|
||||||
.clone()
|
.clone()
|
||||||
.expect("Tried to create recording without work!"),
|
.expect("Tried to create recording without work!"),
|
||||||
comment: self.comment_entry.text().to_string(),
|
self.comment_entry.text().to_string(),
|
||||||
performances: self.performances.borrow().clone(),
|
self.performances.borrow().clone(),
|
||||||
};
|
);
|
||||||
|
|
||||||
self.handle.backend.db().update_recording(recording.clone())?;
|
self.handle
|
||||||
|
.backend
|
||||||
|
.db()
|
||||||
|
.update_recording(recording.clone())?;
|
||||||
self.handle.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
||||||
Ok(recording)
|
Ok(recording)
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,8 @@ impl Screen<Option<Work>, Work> for WorkEditor {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.part_list.set_make_widget_cb(clone!(@weak this => @default-panic, move |index| {
|
this.part_list
|
||||||
|
.set_make_widget_cb(clone!(@weak this => @default-panic, move |index| {
|
||||||
let part = &this.parts.borrow()[index];
|
let part = &this.parts.borrow()[index];
|
||||||
|
|
||||||
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
||||||
|
|
@ -248,17 +249,16 @@ impl WorkEditor {
|
||||||
|
|
||||||
/// Save the work.
|
/// Save the work.
|
||||||
fn save(self: &Rc<Self>) -> Result<Work> {
|
fn save(self: &Rc<Self>) -> Result<Work> {
|
||||||
let work = Work {
|
let work = Work::new(
|
||||||
id: self.id.clone(),
|
self.id.clone(),
|
||||||
title: self.title_entry.text().to_string(),
|
self.title_entry.text().to_string(),
|
||||||
composer: self
|
self.composer
|
||||||
.composer
|
|
||||||
.borrow()
|
.borrow()
|
||||||
.clone()
|
.clone()
|
||||||
.expect("Tried to create work without composer!"),
|
.expect("Tried to create work without composer!"),
|
||||||
instruments: self.instruments.borrow().clone(),
|
self.instruments.borrow().clone(),
|
||||||
parts: self.parts.borrow().clone(),
|
self.parts.borrow().clone(),
|
||||||
};
|
);
|
||||||
|
|
||||||
self.handle.backend.db().update_work(work.clone())?;
|
self.handle.backend.db().update_work(work.clone())?;
|
||||||
self.handle.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
|
||||||
|
|
@ -186,23 +186,23 @@ impl MediumEditor {
|
||||||
|
|
||||||
for track_set_data in &*self.track_sets.borrow() {
|
for track_set_data in &*self.track_sets.borrow() {
|
||||||
for track_data in &track_set_data.tracks {
|
for track_data in &track_set_data.tracks {
|
||||||
let track = Track {
|
let track = Track::new(
|
||||||
recording: track_set_data.recording.clone(),
|
track_set_data.recording.clone(),
|
||||||
work_parts: track_data.work_parts.clone(),
|
track_data.work_parts.clone(),
|
||||||
source_index: track_data.track_source,
|
track_data.track_source,
|
||||||
path: String::new(),
|
String::new(),
|
||||||
};
|
);
|
||||||
|
|
||||||
tracks.push(track);
|
tracks.push(track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let medium = Medium {
|
let medium = Medium::new(
|
||||||
id: generate_id(),
|
generate_id(),
|
||||||
name: self.name_entry.text().to_string(),
|
self.name_entry.text().to_string(),
|
||||||
discid: Some(self.session.source_id().to_owned()),
|
Some(self.session.source_id().to_owned()),
|
||||||
tracks,
|
tracks,
|
||||||
};
|
);
|
||||||
|
|
||||||
// The medium is not added to the database, because the track paths are not known until the
|
// The medium is not added to the database, because the track paths are not known until the
|
||||||
// medium is actually imported into the music library. This step will be handled by the
|
// medium is actually imported into the music library. This step will be handled by the
|
||||||
|
|
|
||||||
|
|
@ -248,12 +248,12 @@ impl MediumPreview {
|
||||||
|
|
||||||
// Add the modified medium to the database.
|
// Add the modified medium to the database.
|
||||||
|
|
||||||
let medium = Medium {
|
let medium = Medium::new(
|
||||||
id: medium.id.clone(),
|
medium.id.clone(),
|
||||||
name: medium.name.clone(),
|
medium.name.clone(),
|
||||||
discid: medium.discid.clone(),
|
medium.discid.clone(),
|
||||||
tracks,
|
tracks,
|
||||||
};
|
);
|
||||||
|
|
||||||
self.handle.backend.db().update_medium(medium)?;
|
self.handle.backend.db().update_medium(medium)?;
|
||||||
self.handle.backend.library_changed();
|
self.handle.backend.library_changed();
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,12 @@ impl Screen<(), Recording> for RecordingSelector {
|
||||||
// immediately show the work editor. Going back from the work editor will
|
// immediately show the work editor. Going back from the work editor will
|
||||||
// correctly show the person selector again.
|
// correctly show the person selector again.
|
||||||
|
|
||||||
let work = Work::new(person);
|
let work = Work::from_composer(person);
|
||||||
if let Some(work) = push!(this.handle, WorkEditor, Some(work)).await {
|
if let Some(work) = push!(this.handle, WorkEditor, Some(work)).await {
|
||||||
// There will also be no existing recordings, so we show the recording
|
// There will also be no existing recordings, so we show the recording
|
||||||
// editor next.
|
// editor next.
|
||||||
|
|
||||||
let recording = Recording::new(work);
|
let recording = Recording::from_work(work);
|
||||||
if let Some(recording) = push!(this.handle, RecordingEditor, Some(recording)).await {
|
if let Some(recording) = push!(this.handle, RecordingEditor, Some(recording)).await {
|
||||||
this.handle.pop(Some(recording));
|
this.handle.pop(Some(recording));
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +117,7 @@ impl Screen<Person, Work> for RecordingSelectorWorkScreen {
|
||||||
|
|
||||||
this.selector.set_add_cb(clone!(@weak this => move || {
|
this.selector.set_add_cb(clone!(@weak this => move || {
|
||||||
spawn!(@clone this, async move {
|
spawn!(@clone this, async move {
|
||||||
let work = Work::new(this.person.clone());
|
let work = Work::from_composer(this.person.clone());
|
||||||
if let Some(work) = push!(this.handle, WorkEditor, Some(work)).await {
|
if let Some(work) = push!(this.handle, WorkEditor, Some(work)).await {
|
||||||
this.handle.pop(Some(work));
|
this.handle.pop(Some(work));
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +180,7 @@ impl Screen<Work, Recording> for RecordingSelectorRecordingScreen {
|
||||||
|
|
||||||
this.selector.set_add_cb(clone!(@weak this => move || {
|
this.selector.set_add_cb(clone!(@weak this => move || {
|
||||||
spawn!(@clone this, async move {
|
spawn!(@clone this, async move {
|
||||||
let recording = Recording::new(this.work.clone());
|
let recording = Recording::from_work(this.work.clone());
|
||||||
if let Some(recording) = push!(this.handle, RecordingEditor, Some(recording)).await {
|
if let Some(recording) = push!(this.handle, RecordingEditor, Some(recording)).await {
|
||||||
this.handle.pop(Some(recording));
|
this.handle.pop(Some(recording));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl Screen<(), Work> for WorkSelector {
|
||||||
// immediately show the work editor. Going back from the work editor will
|
// immediately show the work editor. Going back from the work editor will
|
||||||
// correctly show the person selector again.
|
// correctly show the person selector again.
|
||||||
|
|
||||||
let work = Work::new(person);
|
let work = Work::from_composer(person);
|
||||||
if let Some(work) = push!(this.handle, WorkEditor, Some(work)).await {
|
if let Some(work) = push!(this.handle, WorkEditor, Some(work)).await {
|
||||||
this.handle.pop(Some(work));
|
this.handle.pop(Some(work));
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +107,7 @@ impl Screen<Person, Work> for WorkSelectorWorkScreen {
|
||||||
|
|
||||||
this.selector.set_add_cb(clone!(@weak this => move || {
|
this.selector.set_add_cb(clone!(@weak this => move || {
|
||||||
spawn!(@clone this, async move {
|
spawn!(@clone this, async move {
|
||||||
let work = Work::new(this.person.clone());
|
let work = Work::from_composer(this.person.clone());
|
||||||
if let Some(work) = push!(this.handle, WorkEditor, Some(work)).await {
|
if let Some(work) = push!(this.handle, WorkEditor, Some(work)).await {
|
||||||
this.handle.pop(Some(work));
|
this.handle.pop(Some(work));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue