From 85201cf907f29e2a6a63890296de1521456261bf Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Tue, 30 Aug 2016 23:32:18 -0700 Subject: [PATCH] Added album art detection --- Cargo.lock | 56 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 +--- Polaris.toml | 7 +++-- src/api.rs | 2 ++ src/collection.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++- src/error.rs | 15 +++++----- 6 files changed, 140 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b558eea..c470074 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,6 +4,7 @@ version = "0.1.0" dependencies = [ "iron 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "mount 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)", "router 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "staticfile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -11,6 +12,14 @@ dependencies = [ "url 1.2.0 (git+https://github.com/servo/rust-url)", ] +[[package]] +name = "aho-corasick" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "conduit-mime-types" version = "0.7.3" @@ -141,6 +150,14 @@ name = "matches" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "memchr" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "mime" version = "0.2.2" @@ -179,6 +196,23 @@ dependencies = [ "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "route-recognizer" version = "0.1.11" @@ -237,6 +271,23 @@ dependencies = [ "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread-id" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "time" version = "0.1.35" @@ -325,6 +376,11 @@ dependencies = [ "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index be59382..fd75d4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,8 @@ git = "https://github.com/servo/rust-url" [dependencies] rustc-serialize = "0.3" - -[dependencies] router = "*" mount = "*" staticfile = "*" - -[dependencies] +regex = "0.1" toml = "0.2" \ No newline at end of file diff --git a/Polaris.toml b/Polaris.toml index 984324b..a118e7a 100644 --- a/Polaris.toml +++ b/Polaris.toml @@ -1,3 +1,6 @@ +album_art_pattern = '^Folder\.(png|jpg|jpeg)$' + [[mount_dirs]] -name = "root" -source = "M:/Music/Genres/Electronic/Glitch" +name = 'root' +source = 'M:/Music/Genres/' + diff --git a/src/api.rs b/src/api.rs index d764ee5..c67a0a6 100644 --- a/src/api.rs +++ b/src/api.rs @@ -28,6 +28,8 @@ impl From for IronError { PError::ConfigFileReadError => IronError::new(err, status::InternalServerError), PError::ConfigFileParseError => IronError::new(err, status::InternalServerError), PError::ConfigMountDirsParseError => IronError::new(err, status::InternalServerError), + PError::ConfigAlbumArtPatternParseError => IronError::new(err, status::InternalServerError), + PError::AlbumArtSearchError => IronError::new(err, status::InternalServerError), } } } diff --git a/src/collection.rs b/src/collection.rs index dc825a6..90e3fda 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -3,6 +3,7 @@ use std::fs; use std::fs::File; use std::path::Path; use std::path::PathBuf; +use regex::Regex; use toml; use vfs::*; @@ -12,6 +13,7 @@ use error::*; pub struct Song { path: String, display_name: String, + album_art: String, } impl Song { @@ -23,9 +25,16 @@ impl Song { let display_name = display_name.to_str().unwrap(); let display_name = display_name.to_string(); + let album_art = match collection.get_album_art(path) { + Ok(Some(p)) => try!(collection.vfs.real_to_virtual(p.as_path())), + _ => PathBuf::new(), + }; + let album_art = try!(album_art.to_str().ok_or(PError::PathDecoding)); + Ok(Song { path: path_string.to_string(), display_name: display_name, + album_art: album_art.to_string(), }) } @@ -52,6 +61,7 @@ impl Song { pub struct Directory { path: String, display_name: String, + album_art: String, } impl Directory { @@ -63,9 +73,16 @@ impl Directory { let display_name = display_name.to_str().unwrap(); let display_name = display_name.to_string(); + let album_art = match collection.get_album_art(path) { + Ok(Some(p)) => try!(collection.vfs.real_to_virtual(p.as_path())), + _ => PathBuf::new(), + }; + let album_art = try!(album_art.to_str().ok_or(PError::PathDecoding)); + Ok(Directory { path: path_string.to_string(), display_name: display_name, + album_art: album_art.to_string(), }) } } @@ -78,15 +95,20 @@ pub enum CollectionFile { pub struct Collection { vfs: Vfs, + album_art_pattern: Regex, } const CONFIG_MOUNT_DIRS: &'static str = "mount_dirs"; const CONFIG_MOUNT_DIR_NAME: &'static str = "name"; const CONFIG_MOUNT_DIR_SOURCE: &'static str = "source"; +const CONFIG_ALBUM_ART_PATTERN: &'static str = "album_art_pattern"; impl Collection { pub fn new() -> Collection { - Collection { vfs: Vfs::new() } + Collection { + vfs: Vfs::new(), + album_art_pattern: Regex::new("^Folder\\.png$").unwrap(), + } } pub fn load_config(&mut self, config_path: &Path) -> Result<(), PError> { @@ -112,6 +134,24 @@ impl Collection { // Apply try!(self.load_config_mount_points(&parsed_config)); + try!(self.load_config_album_art_pattern(&parsed_config)); + + Ok(()) + } + + fn load_config_album_art_pattern(&mut self, config: &toml::Table) -> Result<(), PError> { + let pattern = match config.get(CONFIG_ALBUM_ART_PATTERN) { + Some(s) => s, + None => return Ok(()), + }; + let pattern = match pattern { + &toml::Value::String(ref s) => s, + _ => return Err(PError::ConfigAlbumArtPatternParseError), + }; + self.album_art_pattern = match Regex::new(pattern) { + Ok(r) => r, + Err(_) => return Err(PError::ConfigAlbumArtPatternParseError), + }; Ok(()) } @@ -218,4 +258,32 @@ impl Collection { pub fn locate(&self, virtual_path: &Path) -> Result { self.vfs.virtual_to_real(virtual_path) } + + fn get_album_art(&self, real_path: &Path) -> Result, PError> { + let mut real_dir = real_path; + if real_dir.is_file() { + real_dir = try!(real_dir.parent().ok_or(PError::AlbumArtSearchError)); + } + assert!(real_dir.is_dir()); + + let mut files = try!(fs::read_dir(real_dir)); + let album_art = files.find(|dir_entry| { + let file = match *dir_entry { + Err(_) => return false, + Ok(ref r) => r, + }; + let file_name = file.file_name(); + let file_name = match file_name.to_str() { + None => return false, + Some(r) => r, + }; + self.album_art_pattern.is_match(file_name) + }); + + match album_art { + Some(Err(_)) => Err(PError::AlbumArtSearchError), + Some(Ok(a)) => Ok(Some(a.path())), + None => Ok(None), + } + } } diff --git a/src/error.rs b/src/error.rs index 69e9b56..6868c52 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,8 @@ pub enum PError { ConfigFileReadError, ConfigFileParseError, ConfigMountDirsParseError, + ConfigAlbumArtPatternParseError, + AlbumArtSearchError, } impl From for PError { @@ -35,20 +37,15 @@ impl error::Error for PError { PError::ConfigFileReadError => "Could not read config file", PError::ConfigFileParseError => "Could not parse config file", PError::ConfigMountDirsParseError => "Could not parse mount directories in config file", + PError::ConfigAlbumArtPatternParseError => "Could not parse album art pattern in config file", + PError::AlbumArtSearchError => "Error while looking for album art", } } fn cause(&self) -> Option<&error::Error> { match *self { PError::Io(ref err) => Some(err), - PError::PathDecoding => None, - PError::ConflictingMount => None, - PError::PathNotInVfs => None, - PError::CannotServeDirectory => None, - PError::ConfigFileOpenError => None, - PError::ConfigFileReadError => None, - PError::ConfigFileParseError => None, - PError::ConfigMountDirsParseError => None, + _ => None, } } } @@ -67,6 +64,8 @@ impl fmt::Display for PError { PError::ConfigMountDirsParseError => { write!(f, "Could not parse mount directories in config file") } + PError::ConfigAlbumArtPatternParseError => write!(f, "Could not album art pattern in config file"), + PError::AlbumArtSearchError => write!(f, "Error while looking for album art"), } } }