Add CUE support to the library trait

This commit is contained in:
Polochon-street 2022-09-25 00:07:24 +02:00
parent e6ad4c96a6
commit fa3d467536
3 changed files with 185 additions and 52 deletions

View file

@ -131,7 +131,7 @@ impl BlissCueFile {
let song = Song { let song = Song {
path: PathBuf::from(format!( path: PathBuf::from(format!(
"{}/CUE_TRACK{:03}", "{}/CUE_TRACK{:03}",
self.audio_file_path.to_string_lossy(), self.cue_path.to_string_lossy(),
index, index,
)), )),
album: self.album.to_owned(), album: self.album.to_owned(),
@ -210,7 +210,7 @@ mod tests {
let songs = BlissCue::songs_from_path("data/testcue.cue").unwrap(); let songs = BlissCue::songs_from_path("data/testcue.cue").unwrap();
let expected = vec![ let expected = vec![
Ok(Song { Ok(Song {
path: Path::new("data/testcue.flac/CUE_TRACK001").to_path_buf(), path: Path::new("data/testcue.cue/CUE_TRACK001").to_path_buf(),
analysis: Analysis { analysis: Analysis {
internal_analysis: [ internal_analysis: [
0.38463724, 0.38463724,
@ -250,7 +250,7 @@ mod tests {
..Default::default() ..Default::default()
}), }),
Ok(Song { Ok(Song {
path: Path::new("data/testcue.flac/CUE_TRACK002").to_path_buf(), path: Path::new("data/testcue.cue/CUE_TRACK002").to_path_buf(),
analysis: Analysis { analysis: Analysis {
internal_analysis: [ internal_analysis: [
0.18622077, 0.18622077,
@ -290,7 +290,7 @@ mod tests {
..Default::default() ..Default::default()
}), }),
Ok(Song { Ok(Song {
path: Path::new("data/testcue.flac/CUE_TRACK003").to_path_buf(), path: Path::new("data/testcue.cue/CUE_TRACK003").to_path_buf(),
analysis: Analysis { analysis: Analysis {
internal_analysis: [ internal_analysis: [
0.0024261475, 0.0024261475,

View file

@ -306,21 +306,9 @@ mod tests {
)), )),
), ),
(true, PathBuf::from("./data/s16_mono_22_5kHz.flac"), None), (true, PathBuf::from("./data/s16_mono_22_5kHz.flac"), None),
( (true, PathBuf::from("./data/testcue.cue/CUE_TRACK001"), None),
true, (true, PathBuf::from("./data/testcue.cue/CUE_TRACK002"), None),
PathBuf::from("./data/testcue.flac/CUE_TRACK001"), (true, PathBuf::from("./data/testcue.cue/CUE_TRACK003"), None),
None,
),
(
true,
PathBuf::from("./data/testcue.flac/CUE_TRACK002"),
None,
),
(
true,
PathBuf::from("./data/testcue.flac/CUE_TRACK003"),
None,
),
(true, PathBuf::from("./data/white_noise.flac"), None), (true, PathBuf::from("./data/white_noise.flac"), None),
]; ];

View file

@ -23,6 +23,7 @@ use serde::Serialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::fs; use std::fs;
use std::fs::create_dir_all;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
@ -111,16 +112,15 @@ pub struct BaseConfig {
impl BaseConfig { impl BaseConfig {
pub(crate) fn get_default_data_folder() -> Result<PathBuf> { pub(crate) fn get_default_data_folder() -> Result<PathBuf> {
match env::var("XDG_DATA_HOME") { let path = match env::var("XDG_DATA_HOME") {
Ok(path) => Ok(Path::new(&path).join("bliss-rs")), Ok(path) => Path::new(&path).join("bliss-rs"),
Err(_) => { Err(_) => {
Ok(
data_local_dir() data_local_dir()
.with_context(|| "No suitable path found to store bliss' song database. Consider specifying such a path.")? .with_context(|| "No suitable path found to store bliss' song database. Consider specifying such a path.")?
.join("bliss-rs") .join("bliss-rs")
)
}, },
} };
Ok(path)
} }
/// Create a new, basic config. Upon calls of `Config.write()`, it will be /// Create a new, basic config. Upon calls of `Config.write()`, it will be
@ -146,6 +146,7 @@ impl BaseConfig {
Self::get_default_data_folder()?.join(Path::new("songs.db")) Self::get_default_data_folder()?.join(Path::new("songs.db"))
} }
}; };
Ok(Self { Ok(Self {
config_path, config_path,
database_path, database_path,
@ -211,7 +212,9 @@ pub struct LibrarySong<T: Serialize + DeserializeOwned> {
// TODO example LibrarySong without any extra_info // TODO example LibrarySong without any extra_info
// TODO maybe return number of elements updated / deleted / whatev in analysis // TODO maybe return number of elements updated / deleted / whatev in analysis
// functions? // functions?
// TODO add a CUE song to the library, test that getting out CUE songs work // TODO add full rescan
// TODO a song_from_path with custom filters
// TODO "smart" playlist
impl<Config: ConfigTrait> Library<Config> { impl<Config: ConfigTrait> Library<Config> {
/// Create a new [Library] object from the given [Config] struct, /// Create a new [Library] object from the given [Config] struct,
/// writing the configuration to the file given in /// writing the configuration to the file given in
@ -221,6 +224,20 @@ impl<Config: ConfigTrait> Library<Config> {
/// create a completely new "library". /// create a completely new "library".
/// Otherwise, load an existing library file using [Library::from_config]. /// Otherwise, load an existing library file using [Library::from_config].
pub fn new(config: Config) -> Result<Self> { pub fn new(config: Config) -> Result<Self> {
if !config
.base_config()
.config_path
.parent()
.ok_or_else(|| {
BlissError::ProviderError(format!(
"specified path {} is not a valid file path.",
config.base_config().config_path.display()
))
})?
.is_dir()
{
create_dir_all(config.base_config().config_path.parent().unwrap())?;
}
let sqlite_conn = Connection::open(&config.base_config().database_path)?; let sqlite_conn = Connection::open(&config.base_config().database_path)?;
sqlite_conn.execute( sqlite_conn.execute(
" "
@ -365,6 +382,7 @@ impl<Config: ConfigTrait> Library<Config> {
/// Example: /// Example:
/// `library.playlist_from_song_custom(song_path, 20, euclidean_distance, /// `library.playlist_from_song_custom(song_path, 20, euclidean_distance,
/// closest_to_first_song_by_key, true)`. /// closest_to_first_song_by_key, true)`.
/// TODO path here too
pub fn playlist_from_custom<F, G, T: Serialize + DeserializeOwned + std::fmt::Debug>( pub fn playlist_from_custom<F, G, T: Serialize + DeserializeOwned + std::fmt::Debug>(
&self, &self,
song_path: &str, song_path: &str,
@ -377,7 +395,9 @@ impl<Config: ConfigTrait> Library<Config> {
F: FnMut(&LibrarySong<T>, &mut Vec<LibrarySong<T>>, G, fn(&LibrarySong<T>) -> Song), F: FnMut(&LibrarySong<T>, &mut Vec<LibrarySong<T>>, G, fn(&LibrarySong<T>) -> Song),
G: DistanceMetric + Copy, G: DistanceMetric + Copy,
{ {
let first_song: LibrarySong<T> = self.song_from_path(song_path)?; let first_song: LibrarySong<T> = self.song_from_path(song_path).map_err(|_| {
BlissError::ProviderError(format!("song '{}' has not been analyzed", song_path))
})?;
let mut songs = self.songs_from_library()?; let mut songs = self.songs_from_library()?;
sort_by(&first_song, &mut songs, distance, |s: &LibrarySong<T>| { sort_by(&first_song, &mut songs, distance, |s: &LibrarySong<T>| {
s.bliss_song.to_owned() s.bliss_song.to_owned()
@ -475,7 +495,7 @@ impl<Config: ConfigTrait> Library<Config> {
/// ///
/// `convert_extra_info` is a function that you should specify /// `convert_extra_info` is a function that you should specify
/// to convert that extra info to something serializable. /// to convert that extra info to something serializable.
/// // TODO have a `delete` option
pub fn update_library_convert_extra_info< pub fn update_library_convert_extra_info<
T: Serialize + DeserializeOwned, T: Serialize + DeserializeOwned,
U, U,
@ -662,7 +682,6 @@ impl<Config: ConfigTrait> Library<Config> {
convert_extra_info(extra, &song, self) convert_extra_info(extra, &song, self)
} }
}; };
let library_song = LibrarySong::<T> { let library_song = LibrarySong::<T> {
bliss_song: song, bliss_song: song,
extra_info: extra, extra_info: extra,
@ -817,6 +836,7 @@ impl<Config: ConfigTrait> Library<Config> {
} }
/// Get a LibrarySong from a given file path. /// Get a LibrarySong from a given file path.
/// TODO pathbuf here too
pub fn song_from_path<T: Serialize + DeserializeOwned>( pub fn song_from_path<T: Serialize + DeserializeOwned>(
&self, &self,
song_path: &str, song_path: &str,
@ -896,7 +916,8 @@ impl<Config: ConfigTrait> Library<Config> {
cue_info, cue_info,
}; };
let serialized: String = row.get(9).unwrap(); let serialized: Option<String> = row.get(9).unwrap();
let serialized = serialized.unwrap_or_else(|| "null".into());
let extra_info = serde_json::from_str(&serialized).unwrap(); let extra_info = serde_json::from_str(&serialized).unwrap();
Ok(LibrarySong { Ok(LibrarySong {
bliss_song: song, bliss_song: song,
@ -1094,6 +1115,8 @@ mod test {
LibrarySong<ExtraInfo>, LibrarySong<ExtraInfo>,
LibrarySong<ExtraInfo>, LibrarySong<ExtraInfo>,
LibrarySong<ExtraInfo>, LibrarySong<ExtraInfo>,
LibrarySong<ExtraInfo>,
LibrarySong<ExtraInfo>,
), ),
) { ) {
let config_dir = TempDir::new("coucou").unwrap(); let config_dir = TempDir::new("coucou").unwrap();
@ -1242,6 +1265,70 @@ mod test {
}, },
}; };
let analysis_vector: [f32; NUMBER_FEATURES] = (0..NUMBER_FEATURES)
.map(|x| x as f32 * 100.)
.collect::<Vec<f32>>()
.try_into()
.unwrap();
let song = Song {
path: "/path/to/cuetrack.cue/CUE_TRACK001".into(),
artist: Some("CUE Artist".into()),
title: Some("CUE Title 01".into()),
album: Some("CUE Album".into()),
album_artist: Some("CUE Album Artist".into()),
track_number: Some("01".into()),
genre: None,
analysis: Analysis {
internal_analysis: analysis_vector,
},
duration: Duration::from_secs(810),
features_version: 1,
cue_info: Some(CueInfo {
cue_path: PathBuf::from("/path/to/cuetrack.cue"),
audio_file_path: PathBuf::from("/path/to/cuetrack.flac"),
}),
};
let sixth_song = LibrarySong {
bliss_song: song,
extra_info: ExtraInfo {
ignore: false,
metadata_bliss_does_not_have: String::from("/path/to/charlie7001"),
},
};
let analysis_vector: [f32; NUMBER_FEATURES] = (0..NUMBER_FEATURES)
.map(|x| x as f32 * 101.)
.collect::<Vec<f32>>()
.try_into()
.unwrap();
let song = Song {
path: "/path/to/cuetrack.cue/CUE_TRACK002".into(),
artist: Some("CUE Artist".into()),
title: Some("CUE Title 02".into()),
album: Some("CUE Album".into()),
album_artist: Some("CUE Album Artist".into()),
track_number: Some("02".into()),
genre: None,
analysis: Analysis {
internal_analysis: analysis_vector,
},
duration: Duration::from_secs(910),
features_version: 1,
cue_info: Some(CueInfo {
cue_path: PathBuf::from("/path/to/cuetrack.cue"),
audio_file_path: PathBuf::from("/path/to/cuetrack.flac"),
}),
};
let seventh_song = LibrarySong {
bliss_song: song,
extra_info: ExtraInfo {
ignore: false,
metadata_bliss_does_not_have: String::from("/path/to/charlie7001"),
},
};
{ {
let connection = library.sqlite_conn.lock().unwrap(); let connection = library.sqlite_conn.lock().unwrap();
connection connection
@ -1249,59 +1336,75 @@ mod test {
" "
insert into song ( insert into song (
id, path, artist, title, album, album_artist, track_number, id, path, artist, title, album, album_artist, track_number,
genre, duration, analyzed, version, extra_info genre, duration, analyzed, version, extra_info,
cue_path, audio_file_path
) values ( ) values (
1001, '/path/to/song1001', 'Artist1001', 'Title1001', 'An Album1001', 1001, '/path/to/song1001', 'Artist1001', 'Title1001', 'An Album1001',
'An Album Artist1001', '03', 'Electronica1001', 310, true, 'An Album Artist1001', '03', 'Electronica1001', 310, true,
1, '{\"ignore\": true, \"metadata_bliss_does_not_have\": 1, '{\"ignore\": true, \"metadata_bliss_does_not_have\":
\"/path/to/charlie1001\"}' \"/path/to/charlie1001\"}', null, null
), ),
( (
2001, '/path/to/song2001', 'Artist2001', 'Title2001', 'An Album2001', 2001, '/path/to/song2001', 'Artist2001', 'Title2001', 'An Album2001',
'An Album Artist2001', '02', 'Electronica2001', 410, true, 'An Album Artist2001', '02', 'Electronica2001', 410, true,
1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie2001\"}' \"/path/to/charlie2001\"}', null, null
), ),
( (
3001, '/path/to/song3001', null, null, null, 3001, '/path/to/song3001', null, null, null,
null, null, null, null, false, null, null, null, null, false, 1, '{}', null, null
1, '{}'
), ),
( (
4001, '/path/to/song4001', 'Artist4001', 'Title4001', 'An Album4001', 4001, '/path/to/song4001', 'Artist4001', 'Title4001', 'An Album4001',
'An Album Artist4001', '01', 'Electronica4001', 510, true, 'An Album Artist4001', '01', 'Electronica4001', 510, true,
0, '{\"ignore\": false, \"metadata_bliss_does_not_have\": 0, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie4001\"}' \"/path/to/charlie4001\"}', null, null
), ),
( (
5001, '/path/to/song5001', 'Artist5001', 'Title5001', 'An Album1001', 5001, '/path/to/song5001', 'Artist5001', 'Title5001', 'An Album1001',
'An Album Artist5001', '01', 'Electronica5001', 610, true, 'An Album Artist5001', '01', 'Electronica5001', 610, true,
1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie5001\"}' \"/path/to/charlie5001\"}', null, null
), ),
( (
6001, '/path/to/song6001', 'Artist6001', 'Title6001', 'An Album2001', 6001, '/path/to/song6001', 'Artist6001', 'Title6001', 'An Album2001',
'An Album Artist6001', '01', 'Electronica6001', 710, true, 'An Album Artist6001', '01', 'Electronica6001', 710, true,
1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie6001\"}' \"/path/to/charlie6001\"}', null, null
), ),
( (
7001, '/path/to/song7001', 'Artist7001', 'Title7001', 'An Album7001', 7001, '/path/to/song7001', 'Artist7001', 'Title7001', 'An Album7001',
'An Album Artist7001', '01', 'Electronica7001', 810, true, 'An Album Artist7001', '01', 'Electronica7001', 810, true,
1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie7001\"}' \"/path/to/charlie7001\"}', null, null
),
(
7002, '/path/to/cuetrack.cue/CUE_TRACK001', 'CUE Artist',
'CUE Title 01', 'CUE Album',
'CUE Album Artist', '01', null, 810, true,
1, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie7001\"}', '/path/to/cuetrack.cue',
'/path/to/cuetrack.flac'
),
(
7003, '/path/to/cuetrack.cue/CUE_TRACK002', 'CUE Artist',
'CUE Title 02', 'CUE Album',
'CUE Album Artist', '02', null, 910, true,
1, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie7001\"}', '/path/to/cuetrack.cue',
'/path/to/cuetrack.flac'
), ),
( (
8001, '/path/to/song8001', 'Artist8001', 'Title8001', 'An Album1001', 8001, '/path/to/song8001', 'Artist8001', 'Title8001', 'An Album1001',
'An Album Artist8001', '03', 'Electronica8001', 910, true, 'An Album Artist8001', '03', 'Electronica8001', 910, true,
0, '{\"ignore\": false, \"metadata_bliss_does_not_have\": 0, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie8001\"}' \"/path/to/charlie8001\"}', null, null
), ),
( (
9001, './data/s16_stereo_22_5kHz.flac', 'Artist9001', 'Title9001', 9001, './data/s16_stereo_22_5kHz.flac', 'Artist9001', 'Title9001',
'An Album9001', 'An Album Artist8001', '03', 'Electronica8001', 'An Album9001', 'An Album Artist8001', '03', 'Electronica8001',
1010, true, 0, '{\"ignore\": false, \"metadata_bliss_does_not_have\": 1010, true, 0, '{\"ignore\": false, \"metadata_bliss_does_not_have\":
\"/path/to/charlie7001\"}' \"/path/to/charlie7001\"}', null, null
); );
", ",
[], [],
@ -1318,7 +1421,9 @@ mod test {
(3001, ?4, ?1), (3001, ?4, ?1),
(5001, ?5, ?1), (5001, ?5, ?1),
(6001, ?6, ?1), (6001, ?6, ?1),
(7001, ?7, ?1); (7001, ?7, ?1),
(7002, ?8, ?1),
(7003, ?9, ?1);
", ",
params![ params![
index, index,
@ -1328,6 +1433,8 @@ mod test {
index as f32 / 2., index as f32 / 2.,
index as f32 * 0.9, index as f32 * 0.9,
index as f32 * 50., index as f32 * 50.,
index as f32 * 100.,
index as f32 * 101.,
], ],
) )
.unwrap(); .unwrap();
@ -1350,7 +1457,15 @@ mod test {
( (
library, library,
config_dir, config_dir,
(first_song, second_song, third_song, fourth_song, fifth_song), (
first_song,
second_song,
third_song,
fourth_song,
fifth_song,
sixth_song,
seventh_song,
),
) )
} }
@ -1403,7 +1518,7 @@ mod test {
}) })
}, },
) )
.expect("Song probably does not exist in the db."); .expect("Song does not exist in the db.");
let mut stmt = connection let mut stmt = connection
.prepare( .prepare(
" "
@ -1545,6 +1660,8 @@ mod test {
"/path/to/song5001", "/path/to/song5001",
"/path/to/song1001", "/path/to/song1001",
"/path/to/song7001", "/path/to/song7001",
"/path/to/cuetrack.cue/CUE_TRACK001",
"/path/to/cuetrack.cue/CUE_TRACK002",
], ],
songs songs
.into_iter() .into_iter()
@ -1574,6 +1691,8 @@ mod test {
"/path/to/song5001", "/path/to/song5001",
"/path/to/song1001", "/path/to/song1001",
"/path/to/song7001", "/path/to/song7001",
"/path/to/cuetrack.cue/CUE_TRACK001",
"/path/to/cuetrack.cue/CUE_TRACK002",
], ],
songs songs
.into_iter() .into_iter()
@ -1583,15 +1702,14 @@ mod test {
} }
fn custom_sort<F>( fn custom_sort<F>(
first_song: &LibrarySong<ExtraInfo>, _: &LibrarySong<ExtraInfo>,
songs: &mut Vec<LibrarySong<ExtraInfo>>, songs: &mut Vec<LibrarySong<ExtraInfo>>,
_distance: impl DistanceMetric, _distance: impl DistanceMetric,
key_fn: F, key_fn: F,
) where ) where
F: Fn(&LibrarySong<ExtraInfo>) -> Song, F: Fn(&LibrarySong<ExtraInfo>) -> Song,
{ {
let first_song = key_fn(first_song); songs.sort_by_key(|song| key_fn(song).path);
songs.sort_by_cached_key(|song| (&key_fn(song).path).cmp(&first_song.path));
} }
#[test] #[test]
@ -1608,6 +1726,8 @@ mod test {
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
vec![ vec![
"/path/to/cuetrack.cue/CUE_TRACK001",
"/path/to/cuetrack.cue/CUE_TRACK002",
"/path/to/song1001", "/path/to/song1001",
"/path/to/song2001", "/path/to/song2001",
"/path/to/song5001", "/path/to/song5001",
@ -1637,7 +1757,11 @@ mod test {
) )
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
vec!["/path/to/song1001", "/path/to/song7001"], vec![
"/path/to/song1001",
"/path/to/song7001",
"/path/to/cuetrack.cue/CUE_TRACK001"
],
songs songs
.into_iter() .into_iter()
.map(|s| s.bliss_song.path.to_string_lossy().to_string()) .map(|s| s.bliss_song.path.to_string_lossy().to_string())
@ -1662,6 +1786,8 @@ mod test {
"/path/to/song5001", "/path/to/song5001",
"/path/to/song1001", "/path/to/song1001",
"/path/to/song7001", "/path/to/song7001",
"/path/to/cuetrack.cue/CUE_TRACK001",
"/path/to/cuetrack.cue/CUE_TRACK002",
], ],
songs songs
.into_iter() .into_iter()
@ -1686,6 +1812,9 @@ mod test {
"/path/to/song2001".to_string(), "/path/to/song2001".to_string(),
// Third album. // Third album.
"/path/to/song7001".to_string(), "/path/to/song7001".to_string(),
// Fourth album.
"/path/to/cuetrack.cue/CUE_TRACK001".to_string(),
"/path/to/cuetrack.cue/CUE_TRACK002".to_string(),
], ],
album album
.into_iter() .into_iter()
@ -1845,9 +1974,9 @@ mod test {
library.analyze_paths(paths.to_owned(), false).unwrap(); library.analyze_paths(paths.to_owned(), false).unwrap();
let expected_analyzed_paths = vec![ let expected_analyzed_paths = vec![
"./data/s16_mono_22_5kHz.flac", "./data/s16_mono_22_5kHz.flac",
"./data/testcue.flac/CUE_TRACK001", "./data/testcue.cue/CUE_TRACK001",
"./data/testcue.flac/CUE_TRACK002", "./data/testcue.cue/CUE_TRACK002",
"./data/testcue.flac/CUE_TRACK003", "./data/testcue.cue/CUE_TRACK003",
]; ];
{ {
let connection = library.sqlite_conn.lock().unwrap(); let connection = library.sqlite_conn.lock().unwrap();
@ -1871,7 +2000,7 @@ mod test {
{ {
let connection = library.sqlite_conn.lock().unwrap(); let connection = library.sqlite_conn.lock().unwrap();
let song: LibrarySong<()> = let song: LibrarySong<()> =
_library_song_from_database(connection, "./data/testcue.flac/CUE_TRACK001"); _library_song_from_database(connection, "./data/testcue.cue/CUE_TRACK001");
assert!(song.bliss_song.cue_info.is_some()); assert!(song.bliss_song.cue_info.is_some());
} }
} }
@ -2376,7 +2505,7 @@ mod test {
let (library, _temp_dir, expected_library_songs) = setup_test_library(); let (library, _temp_dir, expected_library_songs) = setup_test_library();
let library_songs = library.songs_from_library::<ExtraInfo>().unwrap(); let library_songs = library.songs_from_library::<ExtraInfo>().unwrap();
assert_eq!(library_songs.len(), 5); assert_eq!(library_songs.len(), 7);
assert_eq!( assert_eq!(
expected_library_songs, expected_library_songs,
( (
@ -2385,6 +2514,8 @@ mod test {
library_songs[2].to_owned(), library_songs[2].to_owned(),
library_songs[3].to_owned(), library_songs[3].to_owned(),
library_songs[4].to_owned(), library_songs[4].to_owned(),
library_songs[5].to_owned(),
library_songs[6].to_owned(),
) )
); );
} }
@ -2601,4 +2732,18 @@ mod test {
} }
assert!(library.version_sanity_check().unwrap()); assert!(library.version_sanity_check().unwrap());
} }
#[test]
fn test_library_create_all_dirs() {
let config_dir = TempDir::new("coucou")
.unwrap()
.path()
.join("path")
.join("to");
assert!(!config_dir.is_dir());
let config_file = config_dir.join("config.json");
let database_file = config_dir.join("bliss.db");
Library::<BaseConfig>::new_from_base(Some(config_file), Some(database_file)).unwrap();
assert!(config_dir.is_dir());
}
} }