mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Store tracks from tracks editor
This commit is contained in:
parent
543f5ed1fd
commit
54e743b876
9 changed files with 126 additions and 11 deletions
|
|
@ -17,3 +17,5 @@ DROP TABLE ensembles;
|
||||||
DROP TABLE recordings;
|
DROP TABLE recordings;
|
||||||
|
|
||||||
DROP TABLE performances;
|
DROP TABLE performances;
|
||||||
|
|
||||||
|
DROP TABLE tracks;
|
||||||
|
|
@ -60,3 +60,11 @@ CREATE TABLE performances (
|
||||||
ensemble BIGINT REFERENCES ensembles(id),
|
ensemble BIGINT REFERENCES ensembles(id),
|
||||||
role BIGINT REFERENCES instruments(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
|
||||||
|
);
|
||||||
|
|
@ -34,6 +34,8 @@ enum BackendAction {
|
||||||
GetRecordingsForPerson(i64, Sender<Result<Vec<RecordingDescription>>>),
|
GetRecordingsForPerson(i64, Sender<Result<Vec<RecordingDescription>>>),
|
||||||
GetRecordingsForEnsemble(i64, Sender<Result<Vec<RecordingDescription>>>),
|
GetRecordingsForEnsemble(i64, Sender<Result<Vec<RecordingDescription>>>),
|
||||||
GetRecordingsForWork(i64, Sender<Result<Vec<RecordingDescription>>>),
|
GetRecordingsForWork(i64, Sender<Result<Vec<RecordingDescription>>>),
|
||||||
|
AddTracks(i64, Vec<TrackDescription>, Sender<Result<()>>),
|
||||||
|
GetTracks(i64, Sender<Result<Vec<TrackDescription>>>),
|
||||||
Stop,
|
Stop,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,6 +214,20 @@ impl Backend {
|
||||||
receiver.await?
|
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<()> {
|
pub async fn set_music_library_path(&self, path: PathBuf) -> Result<()> {
|
||||||
self.music_library_path.replace(Some(path.clone()));
|
self.music_library_path.replace(Some(path.clone()));
|
||||||
self.set_state(BackendState::Loading);
|
self.set_state(BackendState::Loading);
|
||||||
|
|
@ -369,6 +385,16 @@ impl Backend {
|
||||||
.send(db.get_recordings_for_work(id))
|
.send(db.get_recordings_for_work(id))
|
||||||
.expect("Failed to send result from database thread!");
|
.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 => {
|
Stop => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use super::schema::*;
|
||||||
use super::tables::*;
|
use super::tables::*;
|
||||||
use anyhow::{anyhow, Error, Result};
|
use anyhow::{anyhow, Error, Result};
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
embed_migrations!();
|
embed_migrations!();
|
||||||
|
|
||||||
|
|
@ -399,6 +400,38 @@ impl Database {
|
||||||
Ok(recordings)
|
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) {
|
fn defer_foreign_keys(&self) {
|
||||||
diesel::sql_query("PRAGMA defer_foreign_keys = ON;")
|
diesel::sql_query("PRAGMA defer_foreign_keys = ON;")
|
||||||
.execute(&self.c)
|
.execute(&self.c)
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,21 @@ impl From<RecordingDescription> for RecordingInsertion {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Track {
|
pub struct TrackDescription {
|
||||||
pub work_parts: Vec<usize>,
|
pub work_parts: Vec<usize>,
|
||||||
pub file_name: String,
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
tracks (id) {
|
||||||
|
id -> BigInt,
|
||||||
|
file_name -> Text,
|
||||||
|
recording -> BigInt,
|
||||||
|
track_index -> Integer,
|
||||||
|
work_parts -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
work_parts (id) {
|
work_parts (id) {
|
||||||
id -> BigInt,
|
id -> BigInt,
|
||||||
|
|
@ -90,6 +100,7 @@ joinable!(performances -> instruments (role));
|
||||||
joinable!(performances -> persons (person));
|
joinable!(performances -> persons (person));
|
||||||
joinable!(performances -> recordings (recording));
|
joinable!(performances -> recordings (recording));
|
||||||
joinable!(recordings -> works (work));
|
joinable!(recordings -> works (work));
|
||||||
|
joinable!(tracks -> recordings (recording));
|
||||||
joinable!(work_parts -> persons (composer));
|
joinable!(work_parts -> persons (composer));
|
||||||
joinable!(work_parts -> works (work));
|
joinable!(work_parts -> works (work));
|
||||||
joinable!(work_sections -> works (work));
|
joinable!(work_sections -> works (work));
|
||||||
|
|
@ -103,6 +114,7 @@ allow_tables_to_appear_in_same_query!(
|
||||||
performances,
|
performances,
|
||||||
persons,
|
persons,
|
||||||
recordings,
|
recordings,
|
||||||
|
tracks,
|
||||||
work_parts,
|
work_parts,
|
||||||
work_sections,
|
work_sections,
|
||||||
works,
|
works,
|
||||||
|
|
|
||||||
|
|
@ -83,3 +83,12 @@ pub struct Performance {
|
||||||
pub ensemble: Option<i64>,
|
pub ensemble: Option<i64>,
|
||||||
pub role: 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,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ pub struct TrackEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
where
|
||||||
W: IsA<gtk::Window>,
|
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");
|
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();
|
let mut work_parts = work_parts.borrow_mut();
|
||||||
work_parts.sort();
|
work_parts.sort();
|
||||||
|
|
||||||
callback(Track {
|
callback(TrackDescription {
|
||||||
work_parts: work_parts.clone(),
|
work_parts: work_parts.clone(),
|
||||||
file_name: file_name.clone(),
|
file_name: file_name.clone(),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,10 @@ impl TracksEditor {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let recording = Rc::new(RefCell::new(None::<RecordingDescription>));
|
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(
|
let track_list = List::new(
|
||||||
clone!(@strong recording => move |track: &Track| {
|
clone!(@strong recording => move |track: &TrackDescription| {
|
||||||
let mut title_parts = Vec::<String>::new();
|
let mut title_parts = Vec::<String>::new();
|
||||||
for part in &track.work_parts {
|
for part in &track.work_parts {
|
||||||
if let Some(recording) = &*recording.borrow() {
|
if let Some(recording) = &*recording.borrow() {
|
||||||
|
|
@ -122,9 +122,20 @@ impl TracksEditor {
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong window => move |_| {
|
let callback = Rc::new(callback);
|
||||||
window.close();
|
save_button.connect_clicked(clone!(@strong window, @strong backend, @strong recording, @strong tracks, @strong callback => move |_| {
|
||||||
callback();
|
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 |_| {
|
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();
|
let mut tracks = tracks.borrow_mut();
|
||||||
for file_name in dialog.get_filenames() {
|
for file_name in dialog.get_filenames() {
|
||||||
let file_name = file_name.strip_prefix(&music_library_path).unwrap();
|
let file_name = file_name.strip_prefix(&music_library_path).unwrap();
|
||||||
tracks.insert(index, Track {
|
tracks.insert(index, TrackDescription {
|
||||||
work_parts: Vec::new(),
|
work_parts: Vec::new(),
|
||||||
file_name: String::from(file_name.to_str().unwrap()),
|
file_name: String::from(file_name.to_str().unwrap()),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue