Store tracks from tracks editor

This commit is contained in:
Elias Projahn 2020-11-01 11:04:31 +01:00
parent 543f5ed1fd
commit 54e743b876
9 changed files with 126 additions and 11 deletions

View file

@ -17,3 +17,5 @@ DROP TABLE ensembles;
DROP TABLE recordings;
DROP TABLE performances;
DROP TABLE tracks;

View file

@ -60,3 +60,11 @@ CREATE TABLE performances (
ensemble BIGINT REFERENCES ensembles(id),
role BIGINT REFERENCES instruments(id)
);
CREATE TABLE tracks (
id BIGINT NOT NULL PRIMARY KEY,
file_name TEXT NOT NULL,
recording BIGINT NOT NULL REFERENCES recordings(id) ON DELETE CASCADE,
track_index INTEGER NOT NULL,
work_parts TEXT NOT NULL
);

View file

@ -34,6 +34,8 @@ enum BackendAction {
GetRecordingsForPerson(i64, Sender<Result<Vec<RecordingDescription>>>),
GetRecordingsForEnsemble(i64, Sender<Result<Vec<RecordingDescription>>>),
GetRecordingsForWork(i64, Sender<Result<Vec<RecordingDescription>>>),
AddTracks(i64, Vec<TrackDescription>, Sender<Result<()>>),
GetTracks(i64, Sender<Result<Vec<TrackDescription>>>),
Stop,
}
@ -212,6 +214,20 @@ impl Backend {
receiver.await?
}
pub async fn add_tracks(&self, recording_id: i64, tracks: Vec<TrackDescription>) -> Result<()> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(AddTracks(recording_id, tracks, sender))?;
receiver.await?
}
pub async fn get_tracks(&self, recording_id: i64) -> Result<Vec<TrackDescription>> {
let (sender, receiver) = oneshot::channel();
self.unwrap_action_sender()?
.send(GetTracks(recording_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);
@ -369,6 +385,16 @@ impl Backend {
.send(db.get_recordings_for_work(id))
.expect("Failed to send result from database thread!");
}
AddTracks(recording_id, tracks, sender) => {
sender
.send(db.add_tracks(recording_id, tracks))
.expect("Failed to send result from database thread!");
}
GetTracks(recording_id, sender) => {
sender
.send(db.get_tracks(recording_id))
.expect("Failed to send result from database thread!");
}
Stop => {
break;
}

View file

@ -3,6 +3,7 @@ use super::schema::*;
use super::tables::*;
use anyhow::{anyhow, Error, Result};
use diesel::prelude::*;
use std::convert::TryInto;
embed_migrations!();
@ -399,6 +400,38 @@ impl Database {
Ok(recordings)
}
pub fn add_tracks(&self, recording_id: i64, tracks: Vec<TrackDescription>) -> Result<()> {
for (index, track_description) in tracks.iter().enumerate() {
let track = Track {
id: rand::random(),
file_name: track_description.file_name.clone(),
recording: recording_id,
track_index: index.try_into().unwrap(),
work_parts: track_description
.work_parts
.iter()
.map(|i| i.to_string())
.collect::<Vec<String>>()
.join(","),
};
diesel::insert_into(tracks::table)
.values(track)
.execute(&self.c)?;
}
Ok(())
}
pub fn get_tracks(&self, recording_id: i64) -> Result<Vec<TrackDescription>> {
let tracks = tracks::table
.filter(tracks::recording.eq(recording_id))
.order_by(tracks::track_index)
.load::<Track>(&self.c)?;
Ok(tracks.iter().map(|track| track.clone().into()).collect())
}
fn defer_foreign_keys(&self) {
diesel::sql_query("PRAGMA defer_foreign_keys = ON;")
.execute(&self.c)

View file

@ -183,7 +183,21 @@ impl From<RecordingDescription> for RecordingInsertion {
}
#[derive(Debug, Clone)]
pub struct Track {
pub struct TrackDescription {
pub work_parts: Vec<usize>,
pub file_name: String,
}
impl From<Track> for TrackDescription {
fn from(track: Track) -> Self {
let mut work_parts = Vec::<usize>::new();
for part in track.work_parts.split(",") {
work_parts.push(part.parse().unwrap());
}
TrackDescription {
work_parts,
file_name: track.file_name,
}
}
}

View file

@ -54,6 +54,16 @@ table! {
}
}
table! {
tracks (id) {
id -> BigInt,
file_name -> Text,
recording -> BigInt,
track_index -> Integer,
work_parts -> Text,
}
}
table! {
work_parts (id) {
id -> BigInt,
@ -90,6 +100,7 @@ joinable!(performances -> instruments (role));
joinable!(performances -> persons (person));
joinable!(performances -> recordings (recording));
joinable!(recordings -> works (work));
joinable!(tracks -> recordings (recording));
joinable!(work_parts -> persons (composer));
joinable!(work_parts -> works (work));
joinable!(work_sections -> works (work));
@ -103,6 +114,7 @@ allow_tables_to_appear_in_same_query!(
performances,
persons,
recordings,
tracks,
work_parts,
work_sections,
works,

View file

@ -83,3 +83,12 @@ pub struct Performance {
pub ensemble: Option<i64>,
pub role: Option<i64>,
}
#[derive(Insertable, Queryable, Debug, Clone)]
pub struct Track {
pub id: i64,
pub file_name: String,
pub recording: i64,
pub track_index: i32,
pub work_parts: String,
}

View file

@ -11,10 +11,10 @@ pub struct TrackEditor {
}
impl TrackEditor {
pub fn new<W, F>(parent: &W, track: Track, work: WorkDescription, callback: F) -> Self
pub fn new<W, F>(parent: &W, track: TrackDescription, work: WorkDescription, callback: F) -> Self
where
W: IsA<gtk::Window>,
F: Fn(Track) -> () + 'static,
F: Fn(TrackDescription) -> () + 'static,
{
let builder = gtk::Builder::from_resource("/de/johrpan/musicus_editor/ui/track_editor.ui");
@ -37,7 +37,7 @@ impl TrackEditor {
let mut work_parts = work_parts.borrow_mut();
work_parts.sort();
callback(Track {
callback(TrackDescription {
work_parts: work_parts.clone(),
file_name: file_name.clone(),
});

View file

@ -41,10 +41,10 @@ impl TracksEditor {
}));
let recording = Rc::new(RefCell::new(None::<RecordingDescription>));
let tracks = Rc::new(RefCell::new(Vec::<Track>::new()));
let tracks = Rc::new(RefCell::new(Vec::<TrackDescription>::new()));
let track_list = List::new(
clone!(@strong recording => move |track: &Track| {
clone!(@strong recording => move |track: &TrackDescription| {
let mut title_parts = Vec::<String>::new();
for part in &track.work_parts {
if let Some(recording) = &*recording.borrow() {
@ -122,9 +122,20 @@ impl TracksEditor {
}
));
save_button.connect_clicked(clone!(@strong window => move |_| {
window.close();
callback();
let callback = Rc::new(callback);
save_button.connect_clicked(clone!(@strong window, @strong backend, @strong recording, @strong tracks, @strong callback => move |_| {
let context = glib::MainContext::default();
let window = window.clone();
let backend = backend.clone();
let recording = recording.clone();
let tracks = tracks.clone();
let callback = callback.clone();
context.spawn_local(async move {
backend.add_tracks(recording.borrow().as_ref().unwrap().id, tracks.borrow().clone()).await.unwrap();
callback();
window.close();
});
}));
add_track_button.connect_clicked(clone!(@strong window, @strong tracks, @strong track_list, @strong autofill_parts => move |_| {
@ -144,7 +155,7 @@ impl TracksEditor {
let mut tracks = tracks.borrow_mut();
for file_name in dialog.get_filenames() {
let file_name = file_name.strip_prefix(&music_library_path).unwrap();
tracks.insert(index, Track {
tracks.insert(index, TrackDescription {
work_parts: Vec::new(),
file_name: String::from(file_name.to_str().unwrap()),
});