diff --git a/data/no-tags-cue.cue b/data/no-tags-cue.cue new file mode 100644 index 0000000..a81520c --- /dev/null +++ b/data/no-tags-cue.cue @@ -0,0 +1,25 @@ +FILE "testcue.flac" WAVE + TRACK 01 AUDIO + TITLE "Renaissance" + PERFORMER "David TMX" + INDEX 01 0:00:00 + TRACK 02 AUDIO + TITLE "Piano" + PERFORMER "Polochon_street" + INDEX 01 0:11:05 + TRACK 03 AUDIO + TITLE "Tone" + PERFORMER "Polochon_street" + INDEX 01 0:16:69 + +FILE "not-existing.wav" WAVE + TRACK 01 AUDIO + TITLE "Nope" + PERFORMER "Charlie" + INDEX 01 0:00:00 + TRACK 02 AUDIO + TITLE "Nope" + PERFORMER "Charlie" + INDEX 01 0:10:00 + + diff --git a/data/s16_mono_22_5kHz.flac b/data/s16_mono_22_5kHz.flac index 7bea0dd..9007c9c 100644 Binary files a/data/s16_mono_22_5kHz.flac and b/data/s16_mono_22_5kHz.flac differ diff --git a/data/testcue.cue b/data/testcue.cue index 544dd83..1577220 100644 --- a/data/testcue.cue +++ b/data/testcue.cue @@ -1,5 +1,6 @@ REM GENRE Random REM DATE 2022 +REM DISCNUMBER 1 PERFORMER "Polochon_street" TITLE "Album for CUE test" FILE "testcue.flac" WAVE diff --git a/data/white_noise.mp3 b/data/white_noise.mp3 index 2bc384b..3ac06fa 100644 Binary files a/data/white_noise.mp3 and b/data/white_noise.mp3 differ diff --git a/src/cue.rs b/src/cue.rs index baffe04..f7a64d8 100644 --- a/src/cue.rs +++ b/src/cue.rs @@ -47,6 +47,7 @@ struct BlissCueFile { album: Option, artist: Option, genre: Option, + disc_number: Option, tracks: Vec, cue_path: PathBuf, audio_file_path: PathBuf, @@ -108,8 +109,17 @@ impl BlissCue { .cue .comments .iter() - .find(|(c, _)| c == "GENRE") + .find(|(c, _)| c.to_uppercase() == "GENRE") .map(|(_, v)| v.to_owned()); + let disc_number = self + .cue + .comments + .iter() + .find(|(c, _)| { + let c_uppercase = c.to_uppercase(); + c_uppercase == "DISCNUMBER" || c_uppercase == "DISC" + }) + .and_then(|(_, v)| v.to_owned().parse::().ok()); let raw_song = D::decode(Path::new(&audio_file_path)); if let Ok(song) = raw_song { let bliss_cue_file = BlissCueFile { @@ -119,6 +129,7 @@ impl BlissCue { album: self.cue.title.to_owned(), tracks: cue_file.tracks.to_owned(), audio_file_path, + disc_number, cue_path: self.cue_path.to_owned(), }; cue_files.push(Ok(bliss_cue_file)) @@ -153,6 +164,7 @@ impl BlissCueFile { genre: self.genre.to_owned(), title: current_track.title.to_owned(), track_number: current_track.no.parse::().ok(), + disc_number: self.disc_number, features_version: FEATURES_VERSION, cue_info: Some(CueInfo { cue_path: self.cue_path.to_owned(), @@ -260,6 +272,7 @@ mod tests { features_version: FEATURES_VERSION, album_artist: Some(String::from("Polochon_street")), duration: Duration::from_secs_f32(11.066666603), + disc_number: Some(1), cue_info: Some(CueInfo { cue_path: PathBuf::from("data/testcue.cue"), audio_file_path: PathBuf::from("data/testcue.flac"), @@ -300,6 +313,7 @@ mod tests { track_number: Some(2), album_artist: Some(String::from("Polochon_street")), duration: Duration::from_secs_f64(5.853333473), + disc_number: Some(1), cue_info: Some(CueInfo { cue_path: PathBuf::from("data/testcue.cue"), audio_file_path: PathBuf::from("data/testcue.flac"), @@ -339,6 +353,7 @@ mod tests { track_number: Some(3), features_version: FEATURES_VERSION, album_artist: Some(String::from("Polochon_street")), + disc_number: Some(1), duration: Duration::from_secs_f32(5.586666584), cue_info: Some(CueInfo { cue_path: PathBuf::from("data/testcue.cue"), @@ -353,4 +368,140 @@ mod tests { ]; assert_eq!(expected, songs); } + + #[test] + #[cfg(feature = "ffmpeg")] + fn test_cue_minimal() { + let songs = BlissCue::::songs_from_path("data/no-tags-cue.cue").unwrap(); + let expected = vec![ + Ok(Song { + path: Path::new("data/no-tags-cue.cue/CUE_TRACK001").to_path_buf(), + analysis: Analysis { + internal_analysis: [ + 0.38463724, + -0.85219246, + -0.761946, + -0.8904667, + -0.63892543, + -0.73945934, + -0.8004017, + -0.8237293, + 0.33865356, + 0.32481194, + -0.35692245, + -0.6355889, + -0.29584837, + 0.06431806, + 0.21875131, + -0.58104205, + -0.9466792, + -0.94811195, + -0.9820919, + -0.9596871, + ], + }, + album: None, + artist: Some(String::from("David TMX")), + title: Some(String::from("Renaissance")), + genre: None, + track_number: Some(1), + features_version: FEATURES_VERSION, + album_artist: None, + duration: Duration::from_secs_f32(11.066666603), + disc_number: None, + cue_info: Some(CueInfo { + cue_path: PathBuf::from("data/no-tags-cue.cue"), + audio_file_path: PathBuf::from("data/testcue.flac"), + }), + ..Default::default() + }), + Ok(Song { + path: Path::new("data/no-tags-cue.cue/CUE_TRACK002").to_path_buf(), + analysis: Analysis { + internal_analysis: [ + 0.18622077, + -0.5989029, + -0.5554645, + -0.6343865, + -0.24163479, + -0.25766593, + -0.40616858, + -0.23334873, + 0.76875293, + 0.7785741, + -0.5075115, + -0.5272629, + -0.56706166, + -0.568486, + -0.5639081, + -0.5706943, + -0.96501005, + -0.96501285, + -0.9649896, + -0.96498996, + ], + }, + features_version: FEATURES_VERSION, + album: None, + artist: Some(String::from("Polochon_street")), + title: Some(String::from("Piano")), + genre: None, + track_number: Some(2), + album_artist: None, + duration: Duration::from_secs_f64(5.853333473), + disc_number: None, + cue_info: Some(CueInfo { + cue_path: PathBuf::from("data/no-tags-cue.cue"), + audio_file_path: PathBuf::from("data/testcue.flac"), + }), + ..Default::default() + }), + Ok(Song { + path: Path::new("data/no-tags-cue.cue/CUE_TRACK003").to_path_buf(), + analysis: Analysis { + internal_analysis: [ + 0.0024261475, + 0.9874661, + 0.97330654, + -0.9724426, + 0.99678576, + -0.9961549, + -0.9840142, + -0.9269961, + 0.7498772, + 0.22429907, + -0.8355152, + -0.9977258, + -0.9977849, + -0.997785, + -0.99778515, + -0.997785, + -0.99999976, + -0.99999976, + -0.99999976, + -0.99999976, + ], + }, + album: None, + artist: Some(String::from("Polochon_street")), + title: Some(String::from("Tone")), + genre: None, + track_number: Some(3), + features_version: FEATURES_VERSION, + album_artist: None, + disc_number: None, + duration: Duration::from_secs_f32(5.586666584), + cue_info: Some(CueInfo { + cue_path: PathBuf::from("data/no-tags-cue.cue"), + audio_file_path: PathBuf::from("data/testcue.flac"), + }), + ..Default::default() + }), + Err(BlissError::DecodingError(String::from( + "while opening format for file 'data/not-existing.wav': \ + ffmpeg::Error(2: No such file or directory).", + ))), + ]; + assert_eq!(expected, songs); + } } diff --git a/src/library.rs b/src/library.rs index cf6e69f..c738892 100644 --- a/src/library.rs +++ b/src/library.rs @@ -369,6 +369,7 @@ impl Library { title text, album text, track_number integer, + disc_number integer, genre text, cue_path text, audio_file_path text, @@ -398,6 +399,7 @@ impl Library { alter table song drop column track_number; alter table song rename column track_number_1 to track_number; ", + "alter table song add column disc_number integer;", ]; /// Create a new [Library] object from the given Config struct that @@ -1001,7 +1003,7 @@ impl Library { let mut songs_statement = connection.prepare(songs_statement)?; let mut features_statement = connection.prepare(features_statement)?; let song_rows = songs_statement.query_map(params.to_owned(), |row| { - Ok((row.get(12)?, Self::_song_from_row_closure(row)?)) + Ok((row.get(13)?, Self::_song_from_row_closure(row)?)) })?; let feature_rows = features_statement.query_map(params, |row| Ok((row.get(1)?, row.get(0)?)))?; @@ -1054,7 +1056,7 @@ impl Library { let songs_statement = " select path, artist, title, album, album_artist, - track_number, genre, duration, version, extra_info, cue_path, + track_number, disc_number, genre, duration, version, extra_info, cue_path, audio_file_path, id from song where analyzed = true and version = ? order by id "; @@ -1079,7 +1081,7 @@ impl Library { let songs_statement = " select path, artist, title, album, album_artist, - track_number, genre, duration, version, extra_info, cue_path, + track_number, disc_number, genre, duration, version, extra_info, cue_path, audio_file_path, id from song where album = ? and analyzed = true and version = ? order @@ -1117,7 +1119,7 @@ impl Library { " select path, artist, title, album, album_artist, - track_number, genre, duration, version, extra_info, + track_number, disc_number, genre, duration, version, extra_info, cue_path, audio_file_path from song where path=? and analyzed = true ", @@ -1155,8 +1157,8 @@ impl Library { ) -> Result, RusqliteError> { let path: String = row.get(0)?; - let cue_path: Option = row.get(10)?; - let audio_file_path: Option = row.get(11)?; + let cue_path: Option = row.get(11)?; + let audio_file_path: Option = row.get(12)?; let mut cue_info = None; if let Some(cue_path) = cue_path { cue_info = Some(CueInfo { @@ -1197,21 +1199,27 @@ impl Library { .as_i64_or_null() .unwrap() .map(|v| v as i32), - genre: row + disc_number: row .get_ref(6) .unwrap() + .as_i64_or_null() + .unwrap() + .map(|v| v as i32), + genre: row + .get_ref(7) + .unwrap() .as_bytes_or_null() .unwrap() .map(|v| String::from_utf8_lossy(v).to_string()), analysis: Analysis { internal_analysis: [0.; NUMBER_FEATURES], }, - duration: Duration::from_secs_f64(row.get(7).unwrap()), - features_version: row.get(8).unwrap(), + duration: Duration::from_secs_f64(row.get(8).unwrap()), + features_version: row.get(9).unwrap(), cue_info, }; - let serialized: Option = row.get(9).unwrap(); + let serialized: Option = row.get(10).unwrap(); let serialized = serialized.unwrap_or_else(|| "null".into()); let extra_info = serde_json::from_str(&serialized).unwrap(); Ok(LibrarySong { @@ -1243,11 +1251,11 @@ impl Library { " insert into song ( path, artist, title, album, album_artist, - duration, track_number, genre, analyzed, version, extra_info, + duration, track_number, disc_number, genre, analyzed, version, extra_info, cue_path, audio_file_path ) values ( - ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13 + ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14 ) on conflict(path) do update set @@ -1255,6 +1263,7 @@ impl Library { title=excluded.title, album=excluded.album, track_number=excluded.track_number, + disc_number=excluded.disc_number, album_artist=excluded.album_artist, duration=excluded.duration, genre=excluded.genre, @@ -1272,6 +1281,7 @@ impl Library { song.album_artist, song.duration.as_secs_f64(), song.track_number, + song.disc_number, song.genre, true, song.features_version, @@ -1500,6 +1510,7 @@ mod test { album: Some("An Album1001".into()), album_artist: Some("An Album Artist1001".into()), track_number: Some(3), + disc_number: Some(1), genre: Some("Electronica1001".into()), analysis: Analysis { internal_analysis: analysis_vector, @@ -1528,6 +1539,7 @@ mod test { album: Some("An Album2001".into()), album_artist: Some("An Album Artist2001".into()), track_number: Some(2), + disc_number: Some(1), genre: Some("Electronica2001".into()), analysis: Analysis { internal_analysis: analysis_vector, @@ -1556,6 +1568,7 @@ mod test { album: Some("Remixes of Album2001".into()), album_artist: Some("An Album Artist2001".into()), track_number: Some(2), + disc_number: Some(1), genre: Some("Electronica2001".into()), analysis: Analysis { internal_analysis: analysis_vector, @@ -1584,6 +1597,7 @@ mod test { album: Some("An Album1001".into()), album_artist: Some("An Album Artist5001".into()), track_number: Some(1), + disc_number: Some(1), genre: Some("Electronica5001".into()), analysis: Analysis { internal_analysis: analysis_vector, @@ -1612,6 +1626,7 @@ mod test { album: Some("An Album2001".into()), album_artist: Some("An Album Artist6001".into()), track_number: Some(1), + disc_number: Some(1), genre: Some("Electronica6001".into()), analysis: Analysis { internal_analysis: analysis_vector, @@ -1640,6 +1655,7 @@ mod test { album: Some("An Album7001".into()), album_artist: Some("An Album Artist7001".into()), track_number: Some(1), + disc_number: Some(1), genre: Some("Electronica7001".into()), analysis: Analysis { internal_analysis: analysis_vector, @@ -1669,6 +1685,7 @@ mod test { album: Some("CUE Album".into()), album_artist: Some("CUE Album Artist".into()), track_number: Some(1), + disc_number: Some(1), genre: None, analysis: Analysis { internal_analysis: analysis_vector, @@ -1701,6 +1718,7 @@ mod test { album: Some("CUE Album".into()), album_artist: Some("CUE Album Artist".into()), track_number: Some(2), + disc_number: Some(1), genre: None, analysis: Analysis { internal_analysis: analysis_vector, @@ -1727,58 +1745,58 @@ mod test { " insert into song ( id, path, artist, title, album, album_artist, track_number, - genre, duration, analyzed, version, extra_info, + disc_number, genre, duration, analyzed, version, extra_info, cue_path, audio_file_path ) values ( 1001, '/path/to/song1001', 'Artist1001', 'Title1001', 'An Album1001', - 'An Album Artist1001', 3, 'Electronica1001', 310, true, + 'An Album Artist1001', 3, 1, 'Electronica1001', 310, true, 1, '{\"ignore\": true, \"metadata_bliss_does_not_have\": \"/path/to/charlie1001\"}', null, null ), ( 2001, '/path/to/song2001', 'Artist2001', 'Title2001', 'An Album2001', - 'An Album Artist2001', 2, 'Electronica2001', 410, true, + 'An Album Artist2001', 2, 1, 'Electronica2001', 410, true, 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie2001\"}', null, null ), ( 2201, '/path/to/song2201', 'Artist2001', 'Title2001', 'Remixes of Album2001', - 'An Album Artist2001', 2, 'Electronica2001', 410, true, + 'An Album Artist2001', 2, 1, 'Electronica2001', 410, true, 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie2201\"}', null, null ), ( 3001, '/path/to/song3001', null, null, null, - null, null, null, null, false, 1, '{}', null, null + null, null, null, null, null, false, 1, '{}', null, null ), ( 4001, '/path/to/song4001', 'Artist4001', 'Title4001', 'An Album4001', - 'An Album Artist4001', 1, 'Electronica4001', 510, true, + 'An Album Artist4001', 1, 1, 'Electronica4001', 510, true, 0, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie4001\"}', null, null ), ( 5001, '/path/to/song5001', 'Artist5001', 'Title5001', 'An Album1001', - 'An Album Artist5001', 1, 'Electronica5001', 610, true, + 'An Album Artist5001', 1, 1, 'Electronica5001', 610, true, 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie5001\"}', null, null ), ( 6001, '/path/to/song6001', 'Artist6001', 'Title6001', 'An Album2001', - 'An Album Artist6001', 1, 'Electronica6001', 710, true, + 'An Album Artist6001', 1, 1, 'Electronica6001', 710, true, 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie6001\"}', null, null ), ( 7001, '/path/to/song7001', 'Artist7001', 'Title7001', 'An Album7001', - 'An Album Artist7001', 1, 'Electronica7001', 810, true, + 'An Album Artist7001', 1, 1, 'Electronica7001', 810, true, 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie7001\"}', null, null ), ( 7002, '/path/to/cuetrack.cue/CUE_TRACK001', 'CUE Artist', 'CUE Title 01', 'CUE Album', - 'CUE Album Artist', 1, null, 810, true, + 'CUE Album Artist', 1, 1, null, 810, true, 1, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie7001\"}', '/path/to/cuetrack.cue', '/path/to/cuetrack.flac' @@ -1786,20 +1804,20 @@ mod test { ( 7003, '/path/to/cuetrack.cue/CUE_TRACK002', 'CUE Artist', 'CUE Title 02', 'CUE Album', - 'CUE Album Artist', 2, null, 910, true, + 'CUE Album Artist', 2, 1, 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', - 'An Album Artist8001', 3, 'Electronica8001', 910, true, + 'An Album Artist8001', 3, 1, 'Electronica8001', 910, true, 0, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie8001\"}', null, null ), ( 9001, './data/s16_stereo_22_5kHz.flac', 'Artist9001', 'Title9001', - 'An Album9001', 'An Album Artist8001', 3, 'Electronica8001', + 'An Album9001', 'An Album Artist8001', 3, 1, 'Electronica8001', 1010, true, 0, '{\"ignore\": false, \"metadata_bliss_does_not_have\": \"/path/to/charlie7001\"}', null, null ); @@ -1878,15 +1896,15 @@ mod test { " select path, artist, title, album, album_artist, - track_number, genre, duration, version, extra_info, + track_number, disc_number, genre, duration, version, extra_info, cue_path, audio_file_path from song where path=? ", params![song_path], |row| { let path: String = row.get(0)?; - let cue_path: Option = row.get(10)?; - let audio_file_path: Option = row.get(11)?; + let cue_path: Option = row.get(11)?; + let audio_file_path: Option = row.get(12)?; let mut cue_info = None; if let Some(cue_path) = cue_path { cue_info = Some(CueInfo { @@ -1901,16 +1919,17 @@ mod test { album: row.get(3).unwrap(), album_artist: row.get(4).unwrap(), track_number: row.get(5).unwrap(), - genre: row.get(6).unwrap(), + disc_number: row.get(6).unwrap(), + genre: row.get(7).unwrap(), analysis: Analysis { internal_analysis: [0.; NUMBER_FEATURES], }, - duration: Duration::from_secs_f64(row.get(7).unwrap()), - features_version: row.get(8).unwrap(), + duration: Duration::from_secs_f64(row.get(8).unwrap()), + features_version: row.get(9).unwrap(), cue_info, }; - let serialized: String = row.get(9).unwrap(); + let serialized: String = row.get(10).unwrap(); let extra_info = serde_json::from_str(&serialized).unwrap(); Ok(LibrarySong { bliss_song: song, @@ -1948,7 +1967,7 @@ mod test { " select path, artist, title, album, album_artist, - track_number, genre, duration, version + track_number, disc_number, genre, duration, version from song where path=? and analyzed = true ", params![song_path], @@ -1961,12 +1980,13 @@ mod test { album: row.get(3).unwrap(), album_artist: row.get(4).unwrap(), track_number: row.get(5).unwrap(), - genre: row.get(6).unwrap(), + disc_number: row.get(6).unwrap(), + genre: row.get(7).unwrap(), analysis: Analysis { internal_analysis: [0.; NUMBER_FEATURES], }, - duration: Duration::from_secs_f64(row.get(7).unwrap()), - features_version: row.get(8).unwrap(), + duration: Duration::from_secs_f64(row.get(8).unwrap()), + features_version: row.get(9).unwrap(), cue_info: None, }) }, @@ -2013,6 +2033,7 @@ mod test { album: Some("An Album".into()), album_artist: Some("An Album Artist".into()), track_number: Some(3), + disc_number: Some(1), genre: Some("Electronica".into()), analysis: Analysis { internal_analysis: analysis_vector, @@ -3081,6 +3102,7 @@ mod test { album: Some("An Album2001".into()), album_artist: Some("An Album Artist2001".into()), track_number: Some(2), + disc_number: Some(1), genre: Some("Electronica2001".into()), analysis: Analysis { internal_analysis: analysis_vector, @@ -3257,12 +3279,12 @@ mod test { " insert into song ( id, path, artist, title, album, album_artist, - track_number, genre, stamp, version, duration, analyzed, + track_number, disc_number, genre, stamp, version, duration, analyzed, extra_info ) values ( 1, '/random/path', 'Some Artist', 'A Title', 'Some Album', - 'Some Album Artist', 1, 'Electronica', '2022-01-01', + 'Some Album Artist', 1, 1, 'Electronica', '2022-01-01', 1, 250, true, '{\"key\": \"value\"}' ); ", @@ -3331,7 +3353,7 @@ mod test { let version: u32 = sqlite_conn .query_row("pragma user_version", [], |row| row.get(0)) .unwrap(); - assert_eq!(version, 2); + assert_eq!(version, 3); // Make sure we can call this over and over without any problem Library::::new_from_base( Some(config_dir.path().join("config.txt")), @@ -3342,7 +3364,7 @@ mod test { let version: u32 = sqlite_conn .query_row("pragma user_version", [], |row| row.get(0)) .unwrap(); - assert_eq!(version, 2); + assert_eq!(version, 3); } #[test] @@ -3366,7 +3388,7 @@ mod test { let version: u32 = sqlite_conn .query_row("pragma user_version", [], |row| row.get(0)) .unwrap(); - assert_eq!(version, 2); + assert_eq!(version, 3); } #[test] diff --git a/src/song/decoder.rs b/src/song/decoder.rs index 9f084fb..1ab94c3 100644 --- a/src/song/decoder.rs +++ b/src/song/decoder.rs @@ -42,6 +42,8 @@ pub struct PreAnalyzedSong { pub album: Option, /// Song's tracked number, read from the metadata pub track_number: Option, + /// Song's disc number, read from the metadata + pub disc_number: Option, /// Song's genre, read from the metadata pub genre: Option, /// The song's duration @@ -71,6 +73,7 @@ impl TryFrom for Song { title: raw_song.title, album: raw_song.album, track_number: raw_song.track_number, + disc_number: raw_song.disc_number, genre: raw_song.genre, duration: raw_song.duration, analysis: Song::analyze(&raw_song.sample_array)?, @@ -512,6 +515,12 @@ pub mod ffmpeg { t => t.parse::().ok(), }; }; + if let Some(disc_number) = ictx.metadata().get("disc") { + song.disc_number = match disc_number { + "" => None, + t => t.parse::().ok(), + }; + }; if let Some(album_artist) = ictx.metadata().get("album_artist") { song.album_artist = match album_artist { "" => None, @@ -666,6 +675,7 @@ pub mod ffmpeg { assert_eq!(song.title, Some(String::from("Renaissance"))); assert_eq!(song.album, Some(String::from("Renaissance"))); assert_eq!(song.track_number, Some(2)); + assert_eq!(song.disc_number, Some(1)); assert_eq!(song.genre, Some(String::from("Pop"))); // Test that there is less than 10ms of difference between what // the song advertises and what we compute. @@ -679,6 +689,7 @@ pub mod ffmpeg { assert_eq!(song.title, None); assert_eq!(song.album, None); assert_eq!(song.track_number, None); + assert_eq!(song.disc_number, None); assert_eq!(song.genre, None); } diff --git a/src/song/mod.rs b/src/song/mod.rs index 92506bf..521307c 100644 --- a/src/song/mod.rs +++ b/src/song/mod.rs @@ -48,6 +48,8 @@ pub struct Song { pub album_artist: Option, /// Song's tracked number, read from the metadata pub track_number: Option, + /// Song's disc number, read from the metadata + pub disc_number: Option, /// Song's genre, read from the metadata pub genre: Option, /// bliss analysis results