library: Add import functionaility

This commit is contained in:
Elias Projahn 2025-03-03 17:28:07 +01:00
parent 14416d49d2
commit cffac0f933
4 changed files with 381 additions and 91 deletions

43
Cargo.lock generated
View file

@ -850,7 +850,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
[[package]]
@ -1490,6 +1502,7 @@ dependencies = [
"once_cell",
"serde",
"serde_json",
"tempfile",
"tracing-subscriber",
"uuid",
"zip",
@ -1771,7 +1784,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@ -2007,13 +2020,13 @@ checksum = "bc1ee6eef34f12f765cb94725905c6312b6610ab2b0940889cfe58dae7bc3c72"
[[package]]
name = "tempfile"
version = "3.15.0"
version = "3.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
dependencies = [
"cfg-if",
"fastrand",
"getrandom",
"getrandom 0.3.1",
"once_cell",
"rustix",
"windows-sys 0.59.0",
@ -2211,7 +2224,7 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@ -2244,6 +2257,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
@ -2424,6 +2446,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags",
]
[[package]]
name = "xdg-home"
version = "1.3.0"

View file

@ -22,6 +22,7 @@ mpris-server = "0.8"
once_cell = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tempfile = "3.17"
tracing-subscriber = "0.3"
uuid = { version = "1", features = ["v4"] }
zip = "2.2"

View file

@ -1,9 +1,10 @@
use std::{
cell::{OnceCell, RefCell},
cell::OnceCell,
ffi::OsString,
fs::{self, File},
io::{BufWriter, Read, Write},
io::{BufReader, BufWriter, Read, Write},
path::{Path, PathBuf},
sync::{Arc, Mutex},
thread,
};
@ -16,6 +17,7 @@ use anyhow::{anyhow, Result};
use chrono::prelude::*;
use diesel::{dsl::exists, prelude::*, QueryDsl, SqliteConnection};
use once_cell::sync::Lazy;
use tempfile::NamedTempFile;
use zip::{write::SimpleFileOptions, ZipWriter};
use crate::{
@ -36,7 +38,7 @@ mod imp {
pub struct Library {
#[property(get, construct_only)]
pub folder: OnceCell<String>,
pub connection: RefCell<Option<SqliteConnection>>,
pub connection: OnceCell<Arc<Mutex<SqliteConnection>>>,
}
#[glib::object_subclass]
@ -59,7 +61,14 @@ mod imp {
let db_path = PathBuf::from(&self.folder.get().unwrap()).join("musicus.db");
let connection = db::connect(db_path.to_str().unwrap()).unwrap();
self.connection.replace(Some(connection));
if self
.connection
.set(Arc::new(Mutex::new(connection)))
.is_err()
{
panic!("connection should not be set");
}
}
}
}
@ -76,8 +85,27 @@ impl Library {
}
/// Import from a library archive.
pub fn import(&self, _path: impl AsRef<Path>) -> Result<()> {
Ok(())
pub fn import(
&self,
path: impl AsRef<Path>,
) -> Result<async_channel::Receiver<LibraryProcessMsg>> {
let path = path.as_ref().to_owned();
let library_folder = PathBuf::from(&self.folder());
let this_connection = self.imp().connection.get().unwrap().clone();
let (sender, receiver) = async_channel::unbounded::<LibraryProcessMsg>();
thread::spawn(move || {
if let Err(err) = sender.send_blocking(LibraryProcessMsg::Result(import_from_zip(
path,
library_folder,
this_connection,
&sender,
))) {
log::error!("Failed to send library action result: {err}");
}
});
Ok(receiver)
}
/// Export the whole music library to an archive at `path`. If `path` already exists, it will
@ -86,8 +114,7 @@ impl Library {
&self,
path: impl AsRef<Path>,
) -> Result<async_channel::Receiver<LibraryProcessMsg>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let path = path.as_ref().to_owned();
let library_folder = PathBuf::from(&self.folder());
@ -110,8 +137,7 @@ impl Library {
pub fn search(&self, query: &LibraryQuery, search: &str) -> Result<LibraryResults> {
let search = format!("%{}%", search);
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
Ok(match query {
LibraryQuery { work: None, .. } => {
@ -486,8 +512,7 @@ impl Library {
}
pub fn generate_recording(&self, program: &Program) -> Result<Recording> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let composer_id = program.composer_id();
let performer_id = program.performer_id();
@ -557,8 +582,7 @@ impl Library {
}
pub fn tracks_for_recording(&self, recording_id: &str) -> Result<Vec<Track>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let tracks = tracks::table
.order(tracks::recording_index)
@ -573,8 +597,7 @@ impl Library {
}
pub fn track_played(&self, track_id: &str) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -599,8 +622,7 @@ impl Library {
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 connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let persons = persons::table
.order(persons::last_used_at.desc())
@ -612,8 +634,7 @@ impl Library {
}
pub fn all_persons(&self) -> Result<Vec<Person>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let persons = persons::table.order(persons::name).load(connection)?;
@ -622,8 +643,7 @@ impl Library {
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 connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let roles = roles::table
.order(roles::last_used_at.desc())
@ -635,8 +655,7 @@ impl Library {
}
pub fn all_roles(&self) -> Result<Vec<Role>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let roles = roles::table.order(roles::name).load(connection)?;
@ -645,8 +664,7 @@ impl Library {
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 connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let instruments = instruments::table
.order(instruments::last_used_at.desc())
@ -658,8 +676,7 @@ impl Library {
}
pub fn all_instruments(&self) -> Result<Vec<Instrument>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let instruments = instruments::table
.order(instruments::name)
@ -670,8 +687,7 @@ impl Library {
pub fn search_works(&self, composer: &Person, search: &str) -> Result<Vec<Work>> {
let search = format!("%{}%", search);
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let works: Vec<Work> = works::table
.left_join(work_persons::table)
@ -693,8 +709,7 @@ impl Library {
pub fn search_recordings(&self, work: &Work, search: &str) -> Result<Vec<Recording>> {
let search = format!("%{}%", search);
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let recordings = recordings::table
.left_join(recording_persons::table.inner_join(persons::table))
@ -718,8 +733,7 @@ impl Library {
}
pub fn all_works(&self) -> Result<Vec<Work>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let works = works::table
.order(works::name)
@ -733,8 +747,7 @@ impl Library {
pub fn search_ensembles(&self, search: &str) -> Result<Vec<Ensemble>> {
let search = format!("%{}%", search);
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let ensembles = ensembles::table
.order(ensembles::last_used_at.desc())
@ -755,8 +768,7 @@ impl Library {
}
pub fn all_ensembles(&self) -> Result<Vec<Ensemble>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let ensembles = ensembles::table
.order(ensembles::name)
@ -769,8 +781,7 @@ impl Library {
}
pub fn all_recordings(&self) -> Result<Vec<Recording>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let recordings = recordings::table
.load::<tables::Recording>(connection)?
@ -782,8 +793,7 @@ impl Library {
}
pub fn all_tracks(&self) -> Result<Vec<Track>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let tracks = tracks::table
.load::<tables::Track>(connection)?
@ -800,8 +810,7 @@ impl Library {
}
pub fn all_albums(&self) -> Result<Vec<Album>> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let albums = albums::table
.load::<tables::Album>(connection)?
@ -813,8 +822,7 @@ impl Library {
}
pub fn composer_default_role(&self) -> Result<Role> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
Ok(roles::table
.filter(roles::role_id.eq("380d7e09eb2f49c1a90db2ba4acb6ffd"))
@ -822,8 +830,7 @@ impl Library {
}
pub fn performer_default_role(&self) -> Result<Role> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
Ok(roles::table
.filter(roles::role_id.eq("28ff0aeb11c041a6916d93e9b4884eef"))
@ -831,8 +838,7 @@ impl Library {
}
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 connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -855,8 +861,7 @@ impl Library {
}
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 connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -875,8 +880,7 @@ impl Library {
}
pub fn create_instrument(&self, name: TranslatedString) -> Result<Instrument> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -899,8 +903,7 @@ impl Library {
}
pub fn update_instrument(&self, id: &str, name: TranslatedString) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -919,8 +922,7 @@ impl Library {
}
pub fn create_role(&self, name: TranslatedString) -> Result<Role> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -942,8 +944,7 @@ impl Library {
}
pub fn update_role(&self, id: &str, name: TranslatedString) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -968,8 +969,7 @@ impl Library {
persons: Vec<Composer>,
instruments: Vec<Instrument>,
) -> Result<Work> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let work =
self.create_work_priv(connection, name, parts, persons, instruments, None, None)?;
@ -1057,8 +1057,7 @@ impl Library {
persons: Vec<Composer>,
instruments: Vec<Instrument>,
) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
self.update_work_priv(
connection,
@ -1177,8 +1176,7 @@ impl Library {
}
pub fn create_ensemble(&self, name: TranslatedString) -> Result<Ensemble> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -1205,8 +1203,7 @@ impl Library {
}
pub fn update_ensemble(&self, id: &str, name: TranslatedString) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -1233,8 +1230,7 @@ impl Library {
performers: Vec<Performer>,
ensembles: Vec<EnsemblePerformer>,
) -> Result<Recording> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let recording_id = db::generate_id();
let now = Local::now().naive_local();
@ -1295,8 +1291,7 @@ impl Library {
performers: Vec<Performer>,
ensembles: Vec<EnsemblePerformer>,
) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -1355,8 +1350,7 @@ impl Library {
name: TranslatedString,
recordings: Vec<Recording>,
) -> Result<Album> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let album_id = db::generate_id();
let now = Local::now().naive_local();
@ -1399,8 +1393,7 @@ impl Library {
name: TranslatedString,
recordings: Vec<Recording>,
) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -1443,8 +1436,7 @@ impl Library {
recording_index: i32,
works: Vec<Work>,
) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let track_id = db::generate_id();
let now = Local::now().naive_local();
@ -1500,8 +1492,7 @@ impl Library {
// 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();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
diesel::delete(track_works::table)
.filter(track_works::track_id.eq(&track.track_id))
@ -1525,8 +1516,7 @@ impl Library {
recording_index: i32,
works: Vec<Work>,
) -> Result<()> {
let mut binding = self.imp().connection.borrow_mut();
let connection = &mut *binding.as_mut().unwrap();
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
let now = Local::now().naive_local();
@ -1566,7 +1556,7 @@ impl Library {
})
}
fn changed(&self) {
pub fn changed(&self) {
let obj = self.clone();
// Note: This is a dirty hack to let the calling function return before
// signal handlers are called. This is neccessary because RefCells
@ -1665,6 +1655,246 @@ fn add_file_to_zip(
Ok(())
}
fn import_from_zip(
zip_path: impl AsRef<Path>,
library_folder: impl AsRef<Path>,
this_connection: Arc<Mutex<SqliteConnection>>,
sender: &async_channel::Sender<LibraryProcessMsg>,
) -> Result<()> {
let now = Local::now().naive_local();
let mut archive = zip::ZipArchive::new(BufReader::new(fs::File::open(zip_path)?))?;
let archive_db_file = archive.by_name("musicus.db")?;
let tmp_db_file = NamedTempFile::new()?;
std::io::copy(
&mut BufReader::new(archive_db_file),
&mut BufWriter::new(tmp_db_file.as_file()),
)?;
let mut other_connection = db::connect(tmp_db_file.path().to_str().unwrap())?;
// Load all metadata from the archive.
let persons = persons::table.load::<tables::Person>(&mut other_connection)?;
let roles = roles::table.load::<tables::Role>(&mut other_connection)?;
let instruments = instruments::table.load::<tables::Instrument>(&mut other_connection)?;
let works = works::table.load::<tables::Work>(&mut other_connection)?;
let work_persons = work_persons::table.load::<tables::WorkPerson>(&mut other_connection)?;
let work_instruments =
work_instruments::table.load::<tables::WorkInstrument>(&mut other_connection)?;
let ensembles = ensembles::table.load::<tables::Ensemble>(&mut other_connection)?;
let ensemble_persons =
ensemble_persons::table.load::<tables::EnsemblePerson>(&mut other_connection)?;
let recordings = recordings::table.load::<tables::Recording>(&mut other_connection)?;
let recording_persons =
recording_persons::table.load::<tables::RecordingPerson>(&mut other_connection)?;
let recording_ensembles =
recording_ensembles::table.load::<tables::RecordingEnsemble>(&mut other_connection)?;
let tracks = tracks::table.load::<tables::Track>(&mut other_connection)?;
let track_works = track_works::table.load::<tables::TrackWork>(&mut other_connection)?;
let mediums = mediums::table.load::<tables::Medium>(&mut other_connection)?;
let albums = albums::table.load::<tables::Album>(&mut other_connection)?;
let album_recordings =
album_recordings::table.load::<tables::AlbumRecording>(&mut other_connection)?;
let album_mediums = album_mediums::table.load::<tables::AlbumMedium>(&mut other_connection)?;
// Import metadata that is not already present.
for mut person in persons {
person.created_at = now;
person.edited_at = now;
person.last_used_at = now;
person.last_played_at = None;
diesel::insert_into(persons::table)
.values(person)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for mut role in roles {
role.created_at = now;
role.edited_at = now;
role.last_used_at = now;
diesel::insert_into(roles::table)
.values(role)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for mut instrument in instruments {
instrument.created_at = now;
instrument.edited_at = now;
instrument.last_used_at = now;
instrument.last_played_at = None;
diesel::insert_into(instruments::table)
.values(instrument)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for mut work in works {
work.created_at = now;
work.edited_at = now;
work.last_used_at = now;
work.last_played_at = None;
diesel::insert_into(works::table)
.values(work)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for work_person in work_persons {
diesel::insert_into(work_persons::table)
.values(work_person)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for work_instrument in work_instruments {
diesel::insert_into(work_instruments::table)
.values(work_instrument)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for mut ensemble in ensembles {
ensemble.created_at = now;
ensemble.edited_at = now;
ensemble.last_used_at = now;
ensemble.last_played_at = None;
diesel::insert_into(ensembles::table)
.values(ensemble)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for ensemble_person in ensemble_persons {
diesel::insert_into(ensemble_persons::table)
.values(ensemble_person)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for mut recording in recordings {
recording.created_at = now;
recording.edited_at = now;
recording.last_used_at = now;
recording.last_played_at = None;
diesel::insert_into(recordings::table)
.values(recording)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for recording_person in recording_persons {
diesel::insert_into(recording_persons::table)
.values(recording_person)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for recording_ensemble in recording_ensembles {
diesel::insert_into(recording_ensembles::table)
.values(recording_ensemble)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for mut track in tracks.clone() {
track.created_at = now;
track.edited_at = now;
track.last_used_at = now;
track.last_played_at = None;
diesel::insert_into(tracks::table)
.values(track)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for track_work in track_works {
diesel::insert_into(track_works::table)
.values(track_work)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for mut medium in mediums {
medium.created_at = now;
medium.edited_at = now;
medium.last_used_at = now;
medium.last_played_at = None;
diesel::insert_into(mediums::table)
.values(medium)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for mut album in albums {
album.created_at = now;
album.edited_at = now;
album.last_used_at = now;
album.last_played_at = None;
diesel::insert_into(albums::table)
.values(album)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for album_recording in album_recordings {
diesel::insert_into(album_recordings::table)
.values(album_recording)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
for album_medium in album_mediums {
diesel::insert_into(album_mediums::table)
.values(album_medium)
.on_conflict_do_nothing()
.execute(&mut *this_connection.lock().unwrap())?;
}
// Import audio files.
let n_tracks = tracks.len();
// TODO: Cross-platform paths?
for (index, track) in tracks.into_iter().enumerate() {
let library_track_file_path = library_folder.as_ref().join(Path::new(&track.path));
// Skip tracks that are already present.
if !fs::exists(&library_track_file_path)? {
if let Some(parent) = library_track_file_path.parent() {
fs::create_dir_all(parent)?;
}
let archive_track_file = archive.by_name(&track.path)?;
let library_track_file = File::create(library_track_file_path)?;
std::io::copy(
&mut BufReader::new(archive_track_file),
&mut BufWriter::new(library_track_file),
)?;
}
// Ignore if the reveiver has been dropped.
let _ = sender.send_blocking(LibraryProcessMsg::Progress(
(index + 1) as f64 / n_tracks as f64,
));
}
Ok(())
}
#[derive(Debug)]
pub enum LibraryProcessMsg {
Progress(f64),

View file

@ -125,8 +125,36 @@ impl LibraryManager {
}
Ok(path) => {
if let Some(path) = path.path() {
if let Err(err) = self.imp().library.get().unwrap().import(path) {
log::error!("Failed to import library from archive: {err}");
match self.imp().library.get().unwrap().import(&path) {
Ok(receiver) => {
let process = Process::new(
&formatx!(
gettext("Importing music library from {}"),
path.file_name()
.map(|f| f.to_string_lossy().into_owned())
.unwrap_or(gettext("archive"))
)
.unwrap(),
receiver,
);
process.connect_finished_notify(clone!(
#[weak(rename_to = obj)]
self,
move |_| {
obj.imp().library.get().unwrap().changed();
}
));
self.imp()
.process_manager
.get()
.unwrap()
.add_process(&process);
self.add_process(&process);
}
Err(err) => log::error!("Failed to import library: {err}"),
}
}
}