diff --git a/Cargo.lock b/Cargo.lock index ecf356e..7838334 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -791,70 +791,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "encoding" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" -dependencies = [ - "encoding-index-japanese", - "encoding-index-korean", - "encoding-index-simpchinese", - "encoding-index-singlebyte", - "encoding-index-tradchinese", -] - -[[package]] -name = "encoding-index-japanese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-korean" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-simpchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-singlebyte" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-tradchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding_index_tests" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" - [[package]] name = "encoding_rs" version = "0.8.26" @@ -1224,15 +1160,13 @@ checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" [[package]] name = "id3" -version = "0.5.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c11bb50ce1568516aefbe4b6564c3feaf15a8e5ccbea90fa652012446ae9bf" +checksum = "f23fa956cb2f3e3e547993eb62b6f76b5babd86c3e7bb4fe1149ef192d90df51" dependencies = [ "bitflags", "byteorder", - "encoding", "flate2", - "lazy_static", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 35c982a..20caa2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ diesel_migrations = { version = "1.4", features = ["sqlite"] } futures-util = { version = "0.3" } getopts = "0.2.15" http = "0.2.2" -id3 = "0.5.1" +id3 = "0.6.3" libsqlite3-sys = { version = "0.18", features = ["bundled", "bundled-windows"], optional = true } lewton = "0.10.1" log = "0.4.5" diff --git a/src/app/index/metadata.rs b/src/app/index/metadata.rs index 6177ffb..9751548 100644 --- a/src/app/index/metadata.rs +++ b/src/app/index/metadata.rs @@ -27,15 +27,46 @@ pub struct SongTags { pub has_artwork: bool, } +impl From<id3::Tag> for SongTags { + fn from(tag: id3::Tag) -> Self { + let artist = tag.artist().map(|s| s.to_string()); + let album_artist = tag.album_artist().map(|s| s.to_string()); + let album = tag.album().map(|s| s.to_string()); + let title = tag.title().map(|s| s.to_string()); + let duration = tag.duration(); + let disc_number = tag.disc(); + let track_number = tag.track(); + let year = tag + .year() + .map(|y| y as i32) + .or_else(|| tag.date_released().map(|d| d.year)) + .or_else(|| tag.date_recorded().map(|d| d.year)); + let has_artwork = tag.pictures().count() > 0; + + SongTags { + artist, + album_artist, + album, + title, + duration, + disc_number, + track_number, + year, + has_artwork, + } + } +} + pub fn read(path: &Path) -> Option<SongTags> { let data = match utils::get_audio_format(path) { Some(AudioFormat::APE) => Some(read_ape(path)), Some(AudioFormat::FLAC) => Some(read_flac(path)), - Some(AudioFormat::MP3) => Some(read_id3(path)), + Some(AudioFormat::MP3) => Some(read_mp3(path)), Some(AudioFormat::MP4) => Some(read_mp4(path)), Some(AudioFormat::MPC) => Some(read_ape(path)), Some(AudioFormat::OGG) => Some(read_vorbis(path)), Some(AudioFormat::OPUS) => Some(read_opus(path)), + Some(AudioFormat::WAVE) => Some(read_wave(path)), None => None, }; match data { @@ -48,49 +79,35 @@ pub fn read(path: &Path) -> Option<SongTags> { } } -fn read_id3(path: &Path) -> Result<SongTags> { - let tag = { - match id3::Tag::read_from_path(&path) { - Ok(t) => Ok(t), - Err(e) => { - if let Some(t) = e.partial_tag { - Ok(t) - } else { - Err(e) - } - } - }? - }; +fn read_mp3(path: &Path) -> Result<SongTags> { + let tag = id3::Tag::read_from_path(&path).or_else(|error| { + if let Some(tag) = error.partial_tag { + Ok(tag) + } else { + Err(error) + } + })?; + let duration = { mp3_duration::from_path(&path) .map(|d| d.as_secs() as u32) .ok() }; - let artist = tag.artist().map(|s| s.to_string()); - let album_artist = tag.album_artist().map(|s| s.to_string()); - let album = tag.album().map(|s| s.to_string()); - let title = tag.title().map(|s| s.to_string()); - let disc_number = tag.disc(); - let track_number = tag.track(); - let year = tag - .year() - .map(|y| y as i32) - .or_else(|| tag.date_released().and_then(|d| Some(d.year))) - .or_else(|| tag.date_recorded().and_then(|d| Some(d.year))); - let has_artwork = tag.pictures().count() > 0; + let mut song_tags: SongTags = tag.into(); + song_tags.duration = duration; // Use duration from mp3_duration instead of from tags. + Ok(song_tags) +} - Ok(SongTags { - artist, - album_artist, - album, - title, - duration, - disc_number, - track_number, - year, - has_artwork, - }) +fn read_wave(path: &Path) -> Result<SongTags> { + let tag = id3::Tag::read_from_wav(&path).or_else(|error| { + if let Some(tag) = error.partial_tag { + Ok(tag) + } else { + Err(error) + } + })?; + Ok(tag.into()) } fn read_ape_string(item: &ape::Item) -> Option<String> { @@ -306,6 +323,10 @@ fn reads_file_metadata() { read(Path::new("test-data/formats/sample.ape")).unwrap(), sample_tags ); + assert_eq!( + read(Path::new("test-data/formats/sample.wav")).unwrap(), + sample_tags + ); } #[test] @@ -325,4 +346,9 @@ fn reads_embedded_artwork() { .unwrap() .has_artwork ); + assert!( + read(Path::new("test-data/artwork/sample.wav")) + .unwrap() + .has_artwork + ); } diff --git a/src/app/thumbnail/read.rs b/src/app/thumbnail/read.rs index e518704..d2470d5 100644 --- a/src/app/thumbnail/read.rs +++ b/src/app/thumbnail/read.rs @@ -9,11 +9,12 @@ pub fn read(image_path: &Path) -> Result<DynamicImage> { match utils::get_audio_format(image_path) { Some(AudioFormat::APE) => read_ape(image_path), Some(AudioFormat::FLAC) => read_flac(image_path), - Some(AudioFormat::MP3) => read_id3(image_path), + Some(AudioFormat::MP3) => read_mp3(image_path), Some(AudioFormat::MP4) => read_mp4(image_path), Some(AudioFormat::MPC) => read_ape(image_path), Some(AudioFormat::OGG) => read_vorbis(image_path), Some(AudioFormat::OPUS) => read_opus(image_path), + Some(AudioFormat::WAVE) => read_wave(image_path), None => Ok(image::open(image_path)?), } } @@ -37,9 +38,19 @@ fn read_flac(path: &Path) -> Result<DynamicImage> { ))) } -fn read_id3(path: &Path) -> Result<DynamicImage> { +fn read_mp3(path: &Path) -> Result<DynamicImage> { let tag = id3::Tag::read_from_path(path)?; + read_id3(&path, &tag) +} + +fn read_wave(path: &Path) -> Result<DynamicImage> { + let tag = id3::Tag::read_from_wav(path)?; + + read_id3(&path, &tag) +} + +fn read_id3(path: &Path, tag: &id3::Tag) -> Result<DynamicImage> { if let Some(p) = tag.pictures().next() { return Ok(image::load_from_memory(&p.data)?); } @@ -117,4 +128,9 @@ fn can_read_artwork_data() { .map(|d| d.to_rgb8()) .ok(); assert_eq!(opus_img, None); + + let wave_img = read(Path::new("test-data/artwork/sample.wav")) + .unwrap() + .to_rgb8(); + assert_eq!(wave_img, embedded_img); } diff --git a/src/utils.rs b/src/utils.rs index e5f1bb7..d745db2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -21,6 +21,7 @@ pub enum AudioFormat { MPC, OGG, OPUS, + WAVE, } pub fn get_audio_format(path: &Path) -> Option<AudioFormat> { @@ -40,6 +41,7 @@ pub fn get_audio_format(path: &Path) -> Option<AudioFormat> { "mpc" => Some(AudioFormat::MPC), "ogg" => Some(AudioFormat::OGG), "opus" => Some(AudioFormat::OPUS), + "wav" => Some(AudioFormat::WAVE), _ => None, } } @@ -51,4 +53,8 @@ fn can_guess_audio_format() { get_audio_format(Path::new("animals/🐷/my🐖file.flac")), Some(AudioFormat::FLAC) ); + assert_eq!( + get_audio_format(Path::new("animals/🐷/my🐖file.wav")), + Some(AudioFormat::WAVE) + ); } diff --git a/test-data/artwork/sample.wav b/test-data/artwork/sample.wav new file mode 100644 index 0000000..a59b8c1 Binary files /dev/null and b/test-data/artwork/sample.wav differ diff --git a/test-data/formats/sample.wav b/test-data/formats/sample.wav new file mode 100644 index 0000000..a323479 Binary files /dev/null and b/test-data/formats/sample.wav differ