From f21e4e055ffe56a34060f9788dd231385dd92233 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 13 Nov 2016 16:45:12 -0800 Subject: [PATCH] Added support for APE metadata --- Cargo.lock | 16 ++++++++++++++ Cargo.toml | 1 + src/api.rs | 2 ++ src/error.rs | 13 +++++++++++ src/index.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 1 + 6 files changed, 92 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4902aac..fd8004a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,7 @@ name = "polaris" version = "0.1.0" dependencies = [ + "ape 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", "id3 0.1.10 (git+https://github.com/jameshurst/rust-id3)", @@ -32,6 +33,14 @@ dependencies = [ "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ape" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" version = "0.7.0" @@ -54,6 +63,11 @@ name = "buf_redux" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "0.5.3" @@ -1041,9 +1055,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" +"checksum ape 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b419c2e36e91776200588f91e24c970d16d34236369136ca819f12dd903c5691" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bodyparser 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07b171b407e583dc8f01011a713f20575a81ac60acecf3b8153012709aeb1fd6" "checksum buf_redux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b115bd9935c68b58f80ff867e1c46942c4aed79e78bcc8c2bc22d50f52bb9099" +"checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d" "checksum conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95ca30253581af809925ef68c2641cc140d6183f43e12e0af4992d53768bd7b8" diff --git a/Cargo.toml b/Cargo.toml index 59f1854..61462e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Antoine Gersant "] ui = [] [dependencies] +ape = "0.1.2" getopts = "0.2.14" hyper = "0.9.10" id3 = { git = "https://github.com/jameshurst/rust-id3" } diff --git a/src/api.rs b/src/api.rs index a8826b3..3885872 100644 --- a/src/api.rs +++ b/src/api.rs @@ -38,6 +38,8 @@ impl From for IronError { } PError::AlbumArtSearchError => IronError::new(err, status::InternalServerError), PError::ImageProcessingError => IronError::new(err, status::InternalServerError), + PError::UnsupportedMetadataFormat => IronError::new(err, status::InternalServerError), + PError::APEParseError => IronError::new(err, status::InternalServerError), PError::ID3ParseError => IronError::new(err, status::InternalServerError), PError::Unauthorized => IronError::new(err, status::Unauthorized), PError::IncorrectCredentials => IronError::new(err, status::BadRequest), diff --git a/src/error.rs b/src/error.rs index 2891f67..9e811a2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,4 @@ +use ape; use std::error; use std::fmt; use std::io; @@ -20,11 +21,19 @@ pub enum PError { ConfigAlbumArtPatternParseError, AlbumArtSearchError, ImageProcessingError, + UnsupportedMetadataFormat, + APEParseError, ID3ParseError, Unauthorized, IncorrectCredentials, } +impl From for PError { + fn from(_: ape::Error) -> PError { + PError::APEParseError + } +} + impl From for PError { fn from(err: io::Error) -> PError { PError::Io(err) @@ -62,6 +71,8 @@ impl error::Error for PError { } PError::AlbumArtSearchError => "Error while looking for album art", PError::ImageProcessingError => "Error while processing image", + PError::UnsupportedMetadataFormat => "Unsupported metadata format", + PError::APEParseError => "Error while reading APE tag", PError::ID3ParseError => "Error while reading ID3 tag", PError::Unauthorized => "Authentication required", PError::IncorrectCredentials => "Incorrect username/password combination", @@ -97,6 +108,8 @@ impl fmt::Display for PError { } PError::AlbumArtSearchError => write!(f, "Error while looking for album art"), PError::ImageProcessingError => write!(f, "Error while processing image"), + PError::UnsupportedMetadataFormat => write!(f, "Unsupported metadata format"), + PError::APEParseError => write!(f, "Error while reading APE tag"), PError::ID3ParseError => write!(f, "Error while reading ID3 tag"), PError::Unauthorized => write!(f, "Authentication required"), PError::IncorrectCredentials => write!(f, "Incorrect username/password combination"), diff --git a/src/index.rs b/src/index.rs index 80fa5e3..bf9e055 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1,4 +1,5 @@ use core::ops::Deref; +use ape; use id3::Tag; use regex::Regex; use sqlite; @@ -10,6 +11,8 @@ use std::thread; use std::time; use error::*; +use utils; +use utils::AudioFormat; use vfs::Vfs; const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 500; // Put 500 insertions in each transaction @@ -32,7 +35,15 @@ struct SongTags { impl SongTags { fn read(path: &Path) -> Result { - let tag = try!(Tag::read_from_path(path)); + match utils::get_audio_format(path) { + Some(AudioFormat::MP3) => SongTags::read_id3(path), + Some(AudioFormat::MPC) => SongTags::read_ape(path), + _ => Err(PError::UnsupportedMetadataFormat), + } + } + + fn read_id3(path: &Path) -> Result { + let tag = try!(Tag::read_from_path(path)); let artist = tag.artist().map(|s| s.to_string()); let album_artist = tag.album_artist().map(|s| s.to_string()); @@ -52,7 +63,53 @@ impl SongTags { track_number: track_number, year: year, }) - } + } + + fn read_ape_string(item: &ape::Item) -> Option { + match item.value { + ape::ItemValue::Text(ref s) => Some(s.clone()), + _ => None, + } + } + + fn read_ape_i32(item: &ape::Item) -> Option { + match item.value { + ape::ItemValue::Text(ref s) => s.parse::().ok(), + _ => None, + } + } + + fn read_ape_track_number(item: &ape::Item) -> Option { + match item.value { + ape::ItemValue::Text(ref s) => { + let format = Regex::new(r#"^\d+"#).unwrap(); + if let Some((start, end)) = format.find(s) { + s[start..end].parse().ok() + } else { + None + } + }, + _ => None, + } + } + + fn read_ape(path: &Path) -> Result { + let tag = try!(ape::read(path)); + let artist = tag.item("Artist").and_then(SongTags::read_ape_string); + let album = tag.item("Album").and_then(SongTags::read_ape_string); + let album_artist = tag.item("Album artist").and_then(SongTags::read_ape_string); + let title = tag.item("Title").and_then(SongTags::read_ape_string); + let year = tag.item("Year").and_then(SongTags::read_ape_i32); + let track_number = tag.item("Track").and_then(SongTags::read_ape_track_number); + Ok(SongTags { + artist: artist, + album_artist: album_artist, + album: album, + title: title, + track_number: track_number, + year: year, + }) + } } #[derive(Debug, RustcEncodable)] diff --git a/src/main.rs b/src/main.rs index ccf259f..1899d43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +extern crate ape; extern crate core; extern crate getopts; extern crate hyper;