Added album art detection

This commit is contained in:
Antoine Gersant 2016-08-30 23:32:18 -07:00
parent 1e2ed34dd1
commit 85201cf907
6 changed files with 140 additions and 15 deletions

56
Cargo.lock generated
View file

@ -4,6 +4,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"iron 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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)", "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]] [[package]]
name = "conduit-mime-types" name = "conduit-mime-types"
version = "0.7.3" version = "0.7.3"
@ -141,6 +150,14 @@ name = "matches"
version = "0.1.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "mime" name = "mime"
version = "0.2.2" version = "0.2.2"
@ -179,6 +196,23 @@ dependencies = [
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "route-recognizer" name = "route-recognizer"
version = "0.1.11" version = "0.1.11"
@ -237,6 +271,23 @@ dependencies = [
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "time" name = "time"
version = "0.1.35" version = "0.1.35"
@ -325,6 +376,11 @@ dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "winapi" name = "winapi"
version = "0.2.8" version = "0.2.8"

View file

@ -11,11 +11,8 @@ git = "https://github.com/servo/rust-url"
[dependencies] [dependencies]
rustc-serialize = "0.3" rustc-serialize = "0.3"
[dependencies]
router = "*" router = "*"
mount = "*" mount = "*"
staticfile = "*" staticfile = "*"
regex = "0.1"
[dependencies]
toml = "0.2" toml = "0.2"

View file

@ -1,3 +1,6 @@
album_art_pattern = '^Folder\.(png|jpg|jpeg)$'
[[mount_dirs]] [[mount_dirs]]
name = "root" name = 'root'
source = "M:/Music/Genres/Electronic/Glitch" source = 'M:/Music/Genres/'

View file

@ -28,6 +28,8 @@ impl From<PError> for IronError {
PError::ConfigFileReadError => IronError::new(err, status::InternalServerError), PError::ConfigFileReadError => IronError::new(err, status::InternalServerError),
PError::ConfigFileParseError => IronError::new(err, status::InternalServerError), PError::ConfigFileParseError => IronError::new(err, status::InternalServerError),
PError::ConfigMountDirsParseError => 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),
} }
} }
} }

View file

@ -3,6 +3,7 @@ use std::fs;
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use regex::Regex;
use toml; use toml;
use vfs::*; use vfs::*;
@ -12,6 +13,7 @@ use error::*;
pub struct Song { pub struct Song {
path: String, path: String,
display_name: String, display_name: String,
album_art: String,
} }
impl Song { impl Song {
@ -23,9 +25,16 @@ impl Song {
let display_name = display_name.to_str().unwrap(); let display_name = display_name.to_str().unwrap();
let display_name = display_name.to_string(); 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 { Ok(Song {
path: path_string.to_string(), path: path_string.to_string(),
display_name: display_name, display_name: display_name,
album_art: album_art.to_string(),
}) })
} }
@ -52,6 +61,7 @@ impl Song {
pub struct Directory { pub struct Directory {
path: String, path: String,
display_name: String, display_name: String,
album_art: String,
} }
impl Directory { impl Directory {
@ -63,9 +73,16 @@ impl Directory {
let display_name = display_name.to_str().unwrap(); let display_name = display_name.to_str().unwrap();
let display_name = display_name.to_string(); 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 { Ok(Directory {
path: path_string.to_string(), path: path_string.to_string(),
display_name: display_name, display_name: display_name,
album_art: album_art.to_string(),
}) })
} }
} }
@ -78,15 +95,20 @@ pub enum CollectionFile {
pub struct Collection { pub struct Collection {
vfs: Vfs, vfs: Vfs,
album_art_pattern: Regex,
} }
const CONFIG_MOUNT_DIRS: &'static str = "mount_dirs"; const CONFIG_MOUNT_DIRS: &'static str = "mount_dirs";
const CONFIG_MOUNT_DIR_NAME: &'static str = "name"; const CONFIG_MOUNT_DIR_NAME: &'static str = "name";
const CONFIG_MOUNT_DIR_SOURCE: &'static str = "source"; const CONFIG_MOUNT_DIR_SOURCE: &'static str = "source";
const CONFIG_ALBUM_ART_PATTERN: &'static str = "album_art_pattern";
impl Collection { impl Collection {
pub fn new() -> 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> { pub fn load_config(&mut self, config_path: &Path) -> Result<(), PError> {
@ -112,6 +134,24 @@ impl Collection {
// Apply // Apply
try!(self.load_config_mount_points(&parsed_config)); 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(()) Ok(())
} }
@ -218,4 +258,32 @@ impl Collection {
pub fn locate(&self, virtual_path: &Path) -> Result<PathBuf, PError> { pub fn locate(&self, virtual_path: &Path) -> Result<PathBuf, PError> {
self.vfs.virtual_to_real(virtual_path) self.vfs.virtual_to_real(virtual_path)
} }
fn get_album_art(&self, real_path: &Path) -> Result<Option<PathBuf>, 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),
}
}
} }

View file

@ -13,6 +13,8 @@ pub enum PError {
ConfigFileReadError, ConfigFileReadError,
ConfigFileParseError, ConfigFileParseError,
ConfigMountDirsParseError, ConfigMountDirsParseError,
ConfigAlbumArtPatternParseError,
AlbumArtSearchError,
} }
impl From<io::Error> for PError { impl From<io::Error> for PError {
@ -35,20 +37,15 @@ impl error::Error for PError {
PError::ConfigFileReadError => "Could not read config file", PError::ConfigFileReadError => "Could not read config file",
PError::ConfigFileParseError => "Could not parse config file", PError::ConfigFileParseError => "Could not parse config file",
PError::ConfigMountDirsParseError => "Could not parse mount directories in 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> { fn cause(&self) -> Option<&error::Error> {
match *self { match *self {
PError::Io(ref err) => Some(err), PError::Io(ref err) => Some(err),
PError::PathDecoding => None, _ => None,
PError::ConflictingMount => None,
PError::PathNotInVfs => None,
PError::CannotServeDirectory => None,
PError::ConfigFileOpenError => None,
PError::ConfigFileReadError => None,
PError::ConfigFileParseError => None,
PError::ConfigMountDirsParseError => None,
} }
} }
} }
@ -67,6 +64,8 @@ impl fmt::Display for PError {
PError::ConfigMountDirsParseError => { PError::ConfigMountDirsParseError => {
write!(f, "Could not parse mount directories in config file") 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"),
} }
} }
} }