Update library should also delete songs
This commit is contained in:
parent
08aba11e39
commit
9cf425a8ba
|
@ -181,7 +181,7 @@ fn main() -> Result<()> {
|
||||||
} else if let Some(sub_m) = matches.subcommand_matches("update") {
|
} else if let Some(sub_m) = matches.subcommand_matches("update") {
|
||||||
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
|
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
|
||||||
let mut library: Library<Config> = Library::from_config_path(config_path)?;
|
let mut library: Library<Config> = Library::from_config_path(config_path)?;
|
||||||
library.update_library(library.song_paths()?, true)?;
|
library.update_library(library.song_paths()?, true, true)?;
|
||||||
} else if let Some(sub_m) = matches.subcommand_matches("playlist") {
|
} else if let Some(sub_m) = matches.subcommand_matches("playlist") {
|
||||||
let song_path = sub_m.value_of("SONG_PATH").unwrap();
|
let song_path = sub_m.value_of("SONG_PATH").unwrap();
|
||||||
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
|
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
|
||||||
|
|
|
@ -199,7 +199,7 @@ fn main() -> Result<()> {
|
||||||
} else if let Some(sub_m) = matches.subcommand_matches("update") {
|
} else if let Some(sub_m) = matches.subcommand_matches("update") {
|
||||||
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
|
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
|
||||||
let mut library: Library<Config> = Library::from_config_path(config_path)?;
|
let mut library: Library<Config> = Library::from_config_path(config_path)?;
|
||||||
library.update_library_extra_info(library.song_paths_info()?, true)?;
|
library.update_library_extra_info(library.song_paths_info()?, true, true)?;
|
||||||
} else if let Some(sub_m) = matches.subcommand_matches("playlist") {
|
} else if let Some(sub_m) = matches.subcommand_matches("playlist") {
|
||||||
let song_path = sub_m.value_of("SONG_PATH").unwrap();
|
let song_path = sub_m.value_of("SONG_PATH").unwrap();
|
||||||
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
|
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
|
||||||
|
|
|
@ -64,7 +64,6 @@
|
||||||
//! ```
|
//! ```
|
||||||
#![cfg_attr(feature = "bench", feature(test))]
|
#![cfg_attr(feature = "bench", feature(test))]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![warn(rustdoc::missing_doc_code_examples)]
|
|
||||||
mod chroma;
|
mod chroma;
|
||||||
pub mod cue;
|
pub mod cue;
|
||||||
#[cfg(feature = "library")]
|
#[cfg(feature = "library")]
|
||||||
|
|
303
src/library.rs
303
src/library.rs
|
@ -123,6 +123,7 @@ use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use noisy_float::prelude::*;
|
use noisy_float::prelude::*;
|
||||||
use rusqlite::params;
|
use rusqlite::params;
|
||||||
|
use rusqlite::params_from_iter;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use rusqlite::OptionalExtension;
|
use rusqlite::OptionalExtension;
|
||||||
use rusqlite::Params;
|
use rusqlite::Params;
|
||||||
|
@ -346,6 +347,8 @@ pub struct LibrarySong<T: Serialize + DeserializeOwned> {
|
||||||
// TODO a song_from_path with custom filters
|
// TODO a song_from_path with custom filters
|
||||||
// TODO "smart" playlist
|
// TODO "smart" playlist
|
||||||
// TODO should it really use anyhow errors?
|
// TODO should it really use anyhow errors?
|
||||||
|
// TODO make sure that the path to string is consistent
|
||||||
|
// TODO make a function that returns a list of all analyzed songs in the db
|
||||||
impl<Config: AppConfigTrait> Library<Config> {
|
impl<Config: AppConfigTrait> Library<Config> {
|
||||||
/// Create a new [Library] object from the given Config struct that
|
/// Create a new [Library] object from the given Config struct that
|
||||||
/// implements the [AppConfigTrait].
|
/// implements the [AppConfigTrait].
|
||||||
|
@ -586,6 +589,12 @@ impl<Config: AppConfigTrait> Library<Config> {
|
||||||
///
|
///
|
||||||
/// Use this function if you don't have any extra data to bundle with each song.
|
/// Use this function if you don't have any extra data to bundle with each song.
|
||||||
///
|
///
|
||||||
|
/// Setting `delete_everything_else` to true will delete the paths that are
|
||||||
|
/// not mentionned in `paths_extra_info` from the database. If you do not
|
||||||
|
/// use it, because you only pass the new paths that need to be analyzed to
|
||||||
|
/// this function, make sure to delete yourself from the database the songs
|
||||||
|
/// that have been deleted from storage.
|
||||||
|
///
|
||||||
/// If your library
|
/// If your library
|
||||||
/// contains CUE files, pass the CUE file path only, and not individual
|
/// contains CUE files, pass the CUE file path only, and not individual
|
||||||
/// CUE track names: passing `vec![file.cue]` will add
|
/// CUE track names: passing `vec![file.cue]` will add
|
||||||
|
@ -593,22 +602,36 @@ impl<Config: AppConfigTrait> Library<Config> {
|
||||||
pub fn update_library<P: Into<PathBuf>>(
|
pub fn update_library<P: Into<PathBuf>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
paths: Vec<P>,
|
paths: Vec<P>,
|
||||||
|
delete_everything_else: bool,
|
||||||
show_progress_bar: bool,
|
show_progress_bar: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let paths_extra_info = paths.into_iter().map(|path| (path, ())).collect::<Vec<_>>();
|
let paths_extra_info = paths.into_iter().map(|path| (path, ())).collect::<Vec<_>>();
|
||||||
self.update_library_convert_extra_info(paths_extra_info, show_progress_bar, |x, _, _| x)
|
self.update_library_convert_extra_info(
|
||||||
|
paths_extra_info,
|
||||||
|
delete_everything_else,
|
||||||
|
show_progress_bar,
|
||||||
|
|x, _, _| x,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Analyze and store all songs in `paths_extra_info` that haven't already
|
/// Analyze and store all songs in `paths_extra_info` that haven't already
|
||||||
/// been analyzed, along with some extra metadata serializable, and known
|
/// been analyzed, along with some extra metadata serializable, and known
|
||||||
/// before song analysis.
|
/// before song analysis.
|
||||||
|
///
|
||||||
|
/// Setting `delete_everything_else` to true will delete the paths that are
|
||||||
|
/// not mentionned in `paths_extra_info` from the database. If you do not
|
||||||
|
/// use it, because you only pass the new paths that need to be analyzed to
|
||||||
|
/// this function, make sure to delete yourself from the database the songs
|
||||||
|
/// that have been deleted from storage.
|
||||||
pub fn update_library_extra_info<T: Serialize + DeserializeOwned, P: Into<PathBuf>>(
|
pub fn update_library_extra_info<T: Serialize + DeserializeOwned, P: Into<PathBuf>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
paths_extra_info: Vec<(P, T)>,
|
paths_extra_info: Vec<(P, T)>,
|
||||||
|
delete_everything_else: bool,
|
||||||
show_progress_bar: bool,
|
show_progress_bar: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.update_library_convert_extra_info(
|
self.update_library_convert_extra_info(
|
||||||
paths_extra_info,
|
paths_extra_info,
|
||||||
|
delete_everything_else,
|
||||||
show_progress_bar,
|
show_progress_bar,
|
||||||
|extra_info, _, _| extra_info,
|
|extra_info, _, _| extra_info,
|
||||||
)
|
)
|
||||||
|
@ -630,9 +653,15 @@ impl<Config: AppConfigTrait> Library<Config> {
|
||||||
/// CUE track names: passing `vec![file.cue]` will add
|
/// CUE track names: passing `vec![file.cue]` will add
|
||||||
/// individual tracks with the `cue_info` field set in the database.
|
/// individual tracks with the `cue_info` field set in the database.
|
||||||
///
|
///
|
||||||
|
/// Setting `delete_everything_else` to true will delete the paths that are
|
||||||
|
/// not mentionned in `paths_extra_info` from the database. If you do not
|
||||||
|
/// use it, because you only pass the new paths that need to be analyzed to
|
||||||
|
/// this function, make sure to delete yourself from the database the songs
|
||||||
|
/// that have been deleted from storage.
|
||||||
|
///
|
||||||
/// `convert_extra_info` is a function that you should specify how
|
/// `convert_extra_info` is a function that you should specify how
|
||||||
/// to convert that extra info to something serializable.
|
/// to convert that extra info to something serializable.
|
||||||
// TODO have a `delete` option
|
// FIXME implement a `delete_non_included` boolean option (maybe come up with a better name?)
|
||||||
pub fn update_library_convert_extra_info<
|
pub fn update_library_convert_extra_info<
|
||||||
T: Serialize + DeserializeOwned,
|
T: Serialize + DeserializeOwned,
|
||||||
U,
|
U,
|
||||||
|
@ -640,6 +669,7 @@ impl<Config: AppConfigTrait> Library<Config> {
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
paths_extra_info: Vec<(P, U)>,
|
paths_extra_info: Vec<(P, U)>,
|
||||||
|
delete_everything_else: bool,
|
||||||
show_progress_bar: bool,
|
show_progress_bar: bool,
|
||||||
convert_extra_info: fn(U, &Song, &Self) -> T,
|
convert_extra_info: fn(U, &Song, &Self) -> T,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -665,9 +695,22 @@ impl<Config: AppConfigTrait> Library<Config> {
|
||||||
return_value
|
return_value
|
||||||
};
|
};
|
||||||
|
|
||||||
let paths_to_analyze = paths_extra_info
|
let paths_extra_info: Vec<_> = paths_extra_info
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(x, y)| (x.into(), y))
|
.map(|(x, y)| (x.into(), y))
|
||||||
|
.collect();
|
||||||
|
let paths: HashSet<_> = paths_extra_info.iter().map(|(p, _)| p.to_owned()).collect();
|
||||||
|
|
||||||
|
if delete_everything_else {
|
||||||
|
let paths_to_delete = existing_paths.difference(&paths);
|
||||||
|
|
||||||
|
self.delete_paths(paths_to_delete)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't use hashsets because we need the extra info here too,
|
||||||
|
// and U might not be hashable.
|
||||||
|
let paths_to_analyze = paths_extra_info
|
||||||
|
.into_iter()
|
||||||
.filter(|(path, _)| !existing_paths.contains(path))
|
.filter(|(path, _)| !existing_paths.contains(path))
|
||||||
.collect::<Vec<(PathBuf, U)>>();
|
.collect::<Vec<(PathBuf, U)>>();
|
||||||
|
|
||||||
|
@ -1179,7 +1222,7 @@ impl<Config: AppConfigTrait> Library<Config> {
|
||||||
/// Delete a song with path `song_path` from the database.
|
/// Delete a song with path `song_path` from the database.
|
||||||
///
|
///
|
||||||
/// Errors out if the song is not in the database.
|
/// Errors out if the song is not in the database.
|
||||||
pub fn delete_song<P: Into<PathBuf>>(&mut self, song_path: P) -> Result<()> {
|
pub fn delete_path<P: Into<PathBuf>>(&mut self, song_path: P) -> Result<()> {
|
||||||
let song_path = song_path.into();
|
let song_path = song_path.into();
|
||||||
let count = self
|
let count = self
|
||||||
.sqlite_conn
|
.sqlite_conn
|
||||||
|
@ -1200,6 +1243,45 @@ impl<Config: AppConfigTrait> Library<Config> {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete a set of songs with paths `song_paths` from the database.
|
||||||
|
///
|
||||||
|
/// Will return Ok(count) even if less songs than expected were deleted from the database.
|
||||||
|
pub fn delete_paths<P: Into<PathBuf>, I: IntoIterator<Item = P>>(
|
||||||
|
&mut self,
|
||||||
|
paths: I,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let song_paths: Vec<String> = paths
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.into().to_string_lossy().to_string())
|
||||||
|
.collect();
|
||||||
|
if song_paths.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
};
|
||||||
|
let count = self
|
||||||
|
.sqlite_conn
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.execute(
|
||||||
|
&format!(
|
||||||
|
"delete from song where path in ({})",
|
||||||
|
repeat_vars(song_paths.len()),
|
||||||
|
),
|
||||||
|
params_from_iter(song_paths),
|
||||||
|
)
|
||||||
|
.map_err(|e| BlissError::ProviderError(e.to_string()))?;
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from
|
||||||
|
// https://docs.rs/rusqlite/latest/rusqlite/struct.ParamsFromIter.html#realistic-use-case
|
||||||
|
fn repeat_vars(count: usize) -> String {
|
||||||
|
assert_ne!(count, 0);
|
||||||
|
let mut s = "?,".repeat(count);
|
||||||
|
// Remove trailing comma
|
||||||
|
s.pop();
|
||||||
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1208,6 +1290,7 @@ fn data_local_dir() -> Option<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
// TODO refactor (especially the helper functions)
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Analysis, NUMBER_FEATURES};
|
use crate::{Analysis, NUMBER_FEATURES};
|
||||||
|
@ -1663,7 +1746,7 @@ mod test {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("Song does not exist in the db.");
|
.expect("Song does not exist in the database");
|
||||||
let mut stmt = connection
|
let mut stmt = connection
|
||||||
.prepare(
|
.prepare(
|
||||||
"
|
"
|
||||||
|
@ -2031,7 +2114,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_library_delete_song_non_existing() {
|
fn test_library_delete_path_non_existing() {
|
||||||
let (mut library, _temp_dir, _) = setup_test_library();
|
let (mut library, _temp_dir, _) = setup_test_library();
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
@ -2052,11 +2135,11 @@ mod test {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(count, 0);
|
assert_eq!(count, 0);
|
||||||
}
|
}
|
||||||
assert!(library.delete_song("not-existing").is_err());
|
assert!(library.delete_path("not-existing").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_library_delete_song() {
|
fn test_library_delete_path() {
|
||||||
let (mut library, _temp_dir, _) = setup_test_library();
|
let (mut library, _temp_dir, _) = setup_test_library();
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
@ -2078,7 +2161,7 @@ mod test {
|
||||||
assert!(count >= 1);
|
assert!(count >= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
library.delete_song("/path/to/song1001").unwrap();
|
library.delete_path("/path/to/song1001").unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
@ -2101,6 +2184,75 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_library_delete_paths() {
|
||||||
|
let (mut library, _temp_dir, _) = setup_test_library();
|
||||||
|
{
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
let count: u32 = connection
|
||||||
|
.query_row(
|
||||||
|
"select count(*) from feature join song on song.id = feature.song_id where song.path in (?1, ?2)",
|
||||||
|
["/path/to/song1001", "/path/to/song2001"],
|
||||||
|
|row| row.get(0),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(count >= 1);
|
||||||
|
let count: u32 = connection
|
||||||
|
.query_row(
|
||||||
|
"select count(*) from song where path in (?1, ?2)",
|
||||||
|
["/path/to/song1001", "/path/to/song2001"],
|
||||||
|
|row| row.get(0),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(count >= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
library
|
||||||
|
.delete_paths(vec!["/path/to/song1001", "/path/to/song2001"])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
let count: u32 = connection
|
||||||
|
.query_row(
|
||||||
|
"select count(*) from feature join song on song.id = feature.song_id where song.path in (?1, ?2)",
|
||||||
|
["/path/to/song1001", "/path/to/song2001"],
|
||||||
|
|row| row.get(0),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(0, count);
|
||||||
|
let count: u32 = connection
|
||||||
|
.query_row(
|
||||||
|
"select count(*) from song where path in (?1, ?2)",
|
||||||
|
["/path/to/song1001", "/path/to/song2001"],
|
||||||
|
|row| row.get(0),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(0, count);
|
||||||
|
// Make sure we did not delete everything else
|
||||||
|
let count: u32 = connection
|
||||||
|
.query_row("select count(*) from feature", [], |row| row.get(0))
|
||||||
|
.unwrap();
|
||||||
|
assert!(count >= 1);
|
||||||
|
let count: u32 = connection
|
||||||
|
.query_row("select count(*) from song", [], |row| row.get(0))
|
||||||
|
.unwrap();
|
||||||
|
assert!(count >= 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_library_delete_paths_empty() {
|
||||||
|
let (mut library, _temp_dir, _) = setup_test_library();
|
||||||
|
assert_eq!(library.delete_paths::<String, _>([]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_library_delete_paths_non_existing() {
|
||||||
|
let (mut library, _temp_dir, _) = setup_test_library();
|
||||||
|
assert_eq!(library.delete_paths(["not-existing"]).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_analyze_paths_cue() {
|
fn test_analyze_paths_cue() {
|
||||||
let (mut library, _temp_dir, _) = setup_test_library();
|
let (mut library, _temp_dir, _) = setup_test_library();
|
||||||
|
@ -2307,15 +2459,17 @@ mod test {
|
||||||
|
|
||||||
for input in vec![
|
for input in vec![
|
||||||
("./data/s16_mono_22_5kHz.flac", true),
|
("./data/s16_mono_22_5kHz.flac", true),
|
||||||
("./data/s16_mono_22_5khz.flac", false),
|
("./data/s16_mono_22_5kHz.flac", false),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
{
|
{
|
||||||
let paths = vec![input.to_owned()];
|
let paths = vec![input.to_owned()];
|
||||||
library
|
library
|
||||||
.update_library_convert_extra_info(paths.to_owned(), false, |b, _, _| ExtraInfo {
|
.update_library_convert_extra_info(paths.to_owned(), true, false, |b, _, _| {
|
||||||
|
ExtraInfo {
|
||||||
ignore: b,
|
ignore: b,
|
||||||
metadata_bliss_does_not_have: String::from("coucou"),
|
metadata_bliss_does_not_have: String::from("coucou"),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let song = {
|
let song = {
|
||||||
|
@ -2339,17 +2493,18 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _get_song_analyzed(connection: MutexGuard<Connection>, path: String) -> bool {
|
fn _get_song_analyzed(
|
||||||
let mut stmt = connection
|
connection: MutexGuard<Connection>,
|
||||||
.prepare(
|
path: String,
|
||||||
|
) -> Result<bool, RusqliteError> {
|
||||||
|
let mut stmt = connection.prepare(
|
||||||
"
|
"
|
||||||
select
|
select
|
||||||
analyzed from song
|
analyzed from song
|
||||||
where song.path = ?
|
where song.path = ?
|
||||||
",
|
",
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
stmt.query_row([path], |row| (row.get(0)))
|
||||||
stmt.query_row([path], |row| row.get(0)).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2381,7 +2536,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
library
|
library
|
||||||
.update_library(vec![path.to_owned()], false)
|
.update_library(vec![path.to_owned()], true, false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
@ -2416,7 +2571,7 @@ mod test {
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
// Make sure that we tried to "update" song4001 with the new features.
|
// Make sure that we tried to "update" song4001 with the new features.
|
||||||
assert!(_get_song_analyzed(connection, "/path/to/song4001".into()));
|
assert!(_get_song_analyzed(connection, "/path/to/song4001".into()).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
let paths = vec![
|
let paths = vec![
|
||||||
|
@ -2425,8 +2580,12 @@ mod test {
|
||||||
"/path/to/song4001",
|
"/path/to/song4001",
|
||||||
"non-existing",
|
"non-existing",
|
||||||
];
|
];
|
||||||
library.update_library(paths.to_owned(), false).unwrap();
|
library
|
||||||
library.update_library(paths.to_owned(), true).unwrap();
|
.update_library(paths.to_owned(), true, false)
|
||||||
|
.unwrap();
|
||||||
|
library
|
||||||
|
.update_library(paths.to_owned(), true, true)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let songs = paths[..2]
|
let songs = paths[..2]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -2448,7 +2607,7 @@ mod test {
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
// Make sure that we tried to "update" song4001 with the new features.
|
// Make sure that we tried to "update" song4001 with the new features.
|
||||||
assert!(!_get_song_analyzed(connection, "/path/to/song4001".into()));
|
assert!(!_get_song_analyzed(connection, "/path/to/song4001".into()).unwrap());
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
library.config.base_config_mut().features_version,
|
library.config.base_config_mut().features_version,
|
||||||
|
@ -2464,7 +2623,7 @@ mod test {
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
// Make sure that we tried to "update" song4001 with the new features.
|
// Make sure that we tried to "update" song4001 with the new features.
|
||||||
assert!(_get_song_analyzed(connection, "/path/to/song4001".into()));
|
assert!(_get_song_analyzed(connection, "/path/to/song4001".into()).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
let paths = vec![
|
let paths = vec![
|
||||||
|
@ -2474,7 +2633,7 @@ mod test {
|
||||||
("non-existing", false),
|
("non-existing", false),
|
||||||
];
|
];
|
||||||
library
|
library
|
||||||
.update_library_extra_info(paths.to_owned(), false)
|
.update_library_extra_info(paths.to_owned(), true, false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let songs = paths[..2]
|
let songs = paths[..2]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -2495,7 +2654,7 @@ mod test {
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
// Make sure that we tried to "update" song4001 with the new features.
|
// Make sure that we tried to "update" song4001 with the new features.
|
||||||
assert!(!_get_song_analyzed(connection, "/path/to/song4001".into()));
|
assert!(!_get_song_analyzed(connection, "/path/to/song4001".into()).unwrap());
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
library.config.base_config_mut().features_version,
|
library.config.base_config_mut().features_version,
|
||||||
|
@ -2511,7 +2670,12 @@ mod test {
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
// Make sure that we tried to "update" song4001 with the new features.
|
// Make sure that we tried to "update" song4001 with the new features.
|
||||||
assert!(_get_song_analyzed(connection, "/path/to/song4001".into()));
|
assert!(_get_song_analyzed(connection, "/path/to/song4001".into()).unwrap());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
// Make sure that all the starting songs are there
|
||||||
|
assert!(_get_song_analyzed(connection, "/path/to/song2001".into()).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
let paths = vec![
|
let paths = vec![
|
||||||
|
@ -2521,7 +2685,7 @@ mod test {
|
||||||
("non-existing", false),
|
("non-existing", false),
|
||||||
];
|
];
|
||||||
library
|
library
|
||||||
.update_library_convert_extra_info(paths.to_owned(), false, |b, _, _| ExtraInfo {
|
.update_library_convert_extra_info(paths.to_owned(), true, false, |b, _, _| ExtraInfo {
|
||||||
ignore: b,
|
ignore: b,
|
||||||
metadata_bliss_does_not_have: String::from("coucou"),
|
metadata_bliss_does_not_have: String::from("coucou"),
|
||||||
})
|
})
|
||||||
|
@ -2557,7 +2721,90 @@ mod test {
|
||||||
{
|
{
|
||||||
let connection = library.sqlite_conn.lock().unwrap();
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
// Make sure that we tried to "update" song4001 with the new features.
|
// Make sure that we tried to "update" song4001 with the new features.
|
||||||
assert!(!_get_song_analyzed(connection, "/path/to/song4001".into()));
|
assert!(!_get_song_analyzed(connection, "/path/to/song4001".into()).unwrap());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
// Make sure that we deleted older songs
|
||||||
|
assert_eq!(
|
||||||
|
rusqlite::Error::QueryReturnedNoRows,
|
||||||
|
_get_song_analyzed(connection, "/path/to/song2001".into()).unwrap_err(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
library.config.base_config_mut().features_version,
|
||||||
|
FEATURES_VERSION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// TODO maybe we can merge / DRY this and the function ⬆
|
||||||
|
fn test_update_convert_extra_info_do_not_delete() {
|
||||||
|
let (mut library, _temp_dir, _) = setup_test_library();
|
||||||
|
library.config.base_config_mut().features_version = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
// Make sure that we tried to "update" song4001 with the new features.
|
||||||
|
assert!(_get_song_analyzed(connection, "/path/to/song4001".into()).unwrap());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
// Make sure that all the starting songs are there
|
||||||
|
assert!(_get_song_analyzed(connection, "/path/to/song2001".into()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let paths = vec![
|
||||||
|
("./data/s16_mono_22_5kHz.flac", true),
|
||||||
|
("./data/s16_stereo_22_5kHz.flac", false),
|
||||||
|
("/path/to/song4001", false),
|
||||||
|
("non-existing", false),
|
||||||
|
];
|
||||||
|
library
|
||||||
|
.update_library_convert_extra_info(paths.to_owned(), false, false, |b, _, _| {
|
||||||
|
ExtraInfo {
|
||||||
|
ignore: b,
|
||||||
|
metadata_bliss_does_not_have: String::from("coucou"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let songs = paths[..2]
|
||||||
|
.iter()
|
||||||
|
.map(|(path, _)| {
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
_library_song_from_database(connection, path)
|
||||||
|
})
|
||||||
|
.collect::<Vec<LibrarySong<ExtraInfo>>>();
|
||||||
|
let expected_songs = paths[..2]
|
||||||
|
.iter()
|
||||||
|
.zip(
|
||||||
|
vec![
|
||||||
|
ExtraInfo {
|
||||||
|
ignore: true,
|
||||||
|
metadata_bliss_does_not_have: String::from("coucou"),
|
||||||
|
},
|
||||||
|
ExtraInfo {
|
||||||
|
ignore: false,
|
||||||
|
metadata_bliss_does_not_have: String::from("coucou"),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.map(|((path, _extra_info), expected_extra_info)| LibrarySong {
|
||||||
|
bliss_song: Song::from_path(path).unwrap(),
|
||||||
|
extra_info: expected_extra_info,
|
||||||
|
})
|
||||||
|
.collect::<Vec<LibrarySong<ExtraInfo>>>();
|
||||||
|
assert_eq!(songs, expected_songs);
|
||||||
|
{
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
// Make sure that we tried to "update" song4001 with the new features.
|
||||||
|
assert!(!_get_song_analyzed(connection, "/path/to/song4001".into()).unwrap());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let connection = library.sqlite_conn.lock().unwrap();
|
||||||
|
// Make sure that we did not delete older songs
|
||||||
|
assert!(_get_song_analyzed(connection, "/path/to/song2001".into()).unwrap());
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
library.config.base_config_mut().features_version,
|
library.config.base_config_mut().features_version,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user