mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Implement tracks import
This commit is contained in:
parent
fca6ce841c
commit
740ad0cf0b
3 changed files with 181 additions and 18 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use super::tracks_editor_track_row::{PathType, TracksEditorTrackData};
|
||||
use super::tracks_editor_track_row::{TrackLocation, TracksEditorTrackData};
|
||||
use crate::{
|
||||
db::models::{Recording, Work},
|
||||
db::models::{Recording, Track, Work},
|
||||
editor::{
|
||||
recording_editor::MusicusRecordingEditor,
|
||||
recording_selector_popover::RecordingSelectorPopover,
|
||||
|
|
@ -37,6 +37,7 @@ mod imp {
|
|||
pub recording: RefCell<Option<Recording>>,
|
||||
pub recordings_popover: OnceCell<RecordingSelectorPopover>,
|
||||
pub track_rows: RefCell<Vec<TracksEditorTrackRow>>,
|
||||
pub removed_tracks: RefCell<Vec<Track>>,
|
||||
|
||||
#[template_child]
|
||||
pub recording_row: TemplateChild<adw::ActionRow>,
|
||||
|
|
@ -196,6 +197,9 @@ impl TracksEditor {
|
|||
self.imp().track_list.remove(&track_row);
|
||||
}
|
||||
|
||||
// Forget previously removed tracks (see above).
|
||||
self.imp().removed_tracks.borrow_mut().clear();
|
||||
|
||||
let tracks = self
|
||||
.library()
|
||||
.tracks_for_recording(&recording.recording_id)
|
||||
|
|
@ -208,8 +212,7 @@ impl TracksEditor {
|
|||
self.add_track_row(
|
||||
recording.clone(),
|
||||
TracksEditorTrackData {
|
||||
track_id: Some(track.track_id),
|
||||
path: PathType::Library(track.path),
|
||||
location: TrackLocation::Library(track.clone()),
|
||||
parts: track.works,
|
||||
},
|
||||
);
|
||||
|
|
@ -246,8 +249,7 @@ impl TracksEditor {
|
|||
self.add_track_row(
|
||||
recording.to_owned(),
|
||||
TracksEditorTrackData {
|
||||
track_id: None,
|
||||
path: PathType::System(path),
|
||||
location: TrackLocation::System(path),
|
||||
parts: next_part,
|
||||
},
|
||||
);
|
||||
|
|
@ -264,6 +266,10 @@ impl TracksEditor {
|
|||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |row| {
|
||||
if let TrackLocation::Library(track) = row.track_data().location {
|
||||
this.imp().removed_tracks.borrow_mut().push(track);
|
||||
}
|
||||
|
||||
this.imp().track_list.remove(row);
|
||||
this.imp().track_rows.borrow_mut().retain(|p| p != row);
|
||||
}
|
||||
|
|
@ -278,7 +284,39 @@ impl TracksEditor {
|
|||
|
||||
#[template_callback]
|
||||
fn save(&self) {
|
||||
// TODO
|
||||
for track in self.imp().removed_tracks.borrow_mut().drain(..) {
|
||||
self.library().delete_track(&track).unwrap();
|
||||
}
|
||||
|
||||
for (index, track_row) in self.imp().track_rows.borrow_mut().drain(..).enumerate() {
|
||||
let track_data = track_row.track_data();
|
||||
|
||||
match track_data.location {
|
||||
TrackLocation::Undefined => {
|
||||
log::error!("Failed to save track: Undefined track location.");
|
||||
}
|
||||
TrackLocation::Library(track) => self
|
||||
.library()
|
||||
.update_track(&track.track_id, index as i32, track_data.parts)
|
||||
.unwrap(),
|
||||
TrackLocation::System(path) => {
|
||||
if let Some(recording) = &*self.imp().recording.borrow() {
|
||||
self.library()
|
||||
.import_track(
|
||||
&path,
|
||||
&recording.recording_id,
|
||||
index as i32,
|
||||
track_data.parts,
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
log::error!("Failed to save track: No recording set.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.imp().track_list.remove(&track_row);
|
||||
}
|
||||
|
||||
self.navigation().pop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
db::models::{Recording, Work},
|
||||
db::models::{Recording, Track, Work},
|
||||
editor::tracks_editor_parts_popover::TracksEditorPartsPopover,
|
||||
library::MusicusLibrary,
|
||||
};
|
||||
|
|
@ -90,10 +90,10 @@ impl TracksEditorTrackRow {
|
|||
|
||||
obj.set_activatable(!recording.work.parts.is_empty());
|
||||
|
||||
obj.set_subtitle(&match &track_data.path {
|
||||
PathType::None => String::new(),
|
||||
PathType::Library(path) => path.to_owned(),
|
||||
PathType::System(path) => {
|
||||
obj.set_subtitle(&match &track_data.location {
|
||||
TrackLocation::Undefined => String::new(),
|
||||
TrackLocation::Library(track) => track.path.clone(),
|
||||
TrackLocation::System(path) => {
|
||||
let format_string = gettext("Import from {}");
|
||||
let file_name = path.file_name().unwrap().to_str().unwrap();
|
||||
match formatx!(&format_string, file_name) {
|
||||
|
|
@ -178,15 +178,14 @@ impl TracksEditorTrackRow {
|
|||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct TracksEditorTrackData {
|
||||
pub track_id: Option<String>,
|
||||
pub path: PathType,
|
||||
pub location: TrackLocation,
|
||||
pub parts: Vec<Work>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub enum PathType {
|
||||
pub enum TrackLocation {
|
||||
#[default]
|
||||
None,
|
||||
Library(String),
|
||||
Undefined,
|
||||
Library(Track),
|
||||
System(PathBuf),
|
||||
}
|
||||
|
|
|
|||
128
src/library.rs
128
src/library.rs
|
|
@ -8,13 +8,15 @@ use adw::{
|
|||
prelude::*,
|
||||
subclass::prelude::*,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::prelude::*;
|
||||
use diesel::{dsl::exists, prelude::*, QueryDsl, SqliteConnection};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use std::{
|
||||
cell::{OnceCell, RefCell},
|
||||
ffi::OsString,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
|
|
@ -1232,6 +1234,130 @@ impl MusicusLibrary {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Import a track into the music library.
|
||||
// TODO: Support mediums, think about albums.
|
||||
pub fn import_track(
|
||||
&self,
|
||||
path: impl AsRef<Path>,
|
||||
recording_id: &str,
|
||||
recording_index: i32,
|
||||
works: Vec<Work>,
|
||||
) -> Result<()> {
|
||||
let mut binding = self.imp().connection.borrow_mut();
|
||||
let connection = &mut *binding.as_mut().unwrap();
|
||||
|
||||
let track_id = db::generate_id();
|
||||
let now = Local::now().naive_local();
|
||||
|
||||
// TODO: Human interpretable filenames?
|
||||
let mut filename = OsString::from(recording_id);
|
||||
filename.push("_");
|
||||
filename.push(OsString::from(format!("{recording_index:02}")));
|
||||
if let Some(extension) = path.as_ref().extension() {
|
||||
filename.push(".");
|
||||
filename.push(extension);
|
||||
};
|
||||
|
||||
let mut to_path = PathBuf::from(self.folder());
|
||||
to_path.push(&filename);
|
||||
let library_path = filename
|
||||
.into_string()
|
||||
.or(Err(anyhow!("Filename contains invalid Unicode.")))?;
|
||||
|
||||
fs::copy(path, to_path)?;
|
||||
|
||||
let track_data = tables::Track {
|
||||
track_id: track_id.clone(),
|
||||
recording_id: recording_id.to_owned(),
|
||||
recording_index,
|
||||
medium_id: None,
|
||||
medium_index: None,
|
||||
path: library_path,
|
||||
created_at: now,
|
||||
edited_at: now,
|
||||
last_used_at: now,
|
||||
last_played_at: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(tracks::table)
|
||||
.values(&track_data)
|
||||
.execute(connection)?;
|
||||
|
||||
for (index, work) in works.into_iter().enumerate() {
|
||||
let track_work_data = tables::TrackWork {
|
||||
track_id: track_id.clone(),
|
||||
work_id: work.work_id,
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
diesel::insert_into(track_works::table)
|
||||
.values(&track_work_data)
|
||||
.execute(connection)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Support mediums, think about albums.
|
||||
pub fn delete_track(&self, track: &Track) -> Result<()> {
|
||||
let mut binding = self.imp().connection.borrow_mut();
|
||||
let connection = &mut *binding.as_mut().unwrap();
|
||||
|
||||
diesel::delete(track_works::table)
|
||||
.filter(track_works::track_id.eq(&track.track_id))
|
||||
.execute(connection)?;
|
||||
|
||||
diesel::delete(tracks::table)
|
||||
.filter(tracks::track_id.eq(&track.track_id))
|
||||
.execute(connection)?;
|
||||
|
||||
let mut path = PathBuf::from(self.folder());
|
||||
path.push(&track.path);
|
||||
fs::remove_file(path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Support mediums, think about albums.
|
||||
pub fn update_track(
|
||||
&self,
|
||||
track_id: &str,
|
||||
recording_index: i32,
|
||||
works: Vec<Work>,
|
||||
) -> Result<()> {
|
||||
let mut binding = self.imp().connection.borrow_mut();
|
||||
let connection = &mut *binding.as_mut().unwrap();
|
||||
|
||||
let now = Local::now().naive_local();
|
||||
|
||||
diesel::update(tracks::table)
|
||||
.filter(tracks::track_id.eq(track_id.to_owned()))
|
||||
.set((
|
||||
tracks::recording_index.eq(recording_index),
|
||||
tracks::edited_at.eq(now),
|
||||
tracks::last_used_at.eq(now),
|
||||
))
|
||||
.execute(connection)?;
|
||||
|
||||
diesel::delete(track_works::table)
|
||||
.filter(track_works::track_id.eq(track_id))
|
||||
.execute(connection)?;
|
||||
|
||||
for (index, work) in works.into_iter().enumerate() {
|
||||
let track_work_data = tables::TrackWork {
|
||||
track_id: track_id.to_owned(),
|
||||
work_id: work.work_id,
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
diesel::insert_into(track_works::table)
|
||||
.values(&track_work_data)
|
||||
.execute(connection)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn connect_changed<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("changed", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue