Moved metadata decoding to a separate module
This commit is contained in:
parent
b5d9bca981
commit
c3e5e27f23
5 changed files with 145 additions and 146 deletions
|
@ -39,9 +39,7 @@ impl From<PError> 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::VorbisParseError => IronError::new(err, status::InternalServerError),
|
||||
PError::MetadataDecodingError => IronError::new(err, status::InternalServerError),
|
||||
PError::Unauthorized => IronError::new(err, status::Unauthorized),
|
||||
PError::IncorrectCredentials => IronError::new(err, status::BadRequest),
|
||||
}
|
||||
|
|
18
src/error.rs
18
src/error.rs
|
@ -23,16 +23,14 @@ pub enum PError {
|
|||
AlbumArtSearchError,
|
||||
ImageProcessingError,
|
||||
UnsupportedMetadataFormat,
|
||||
APEParseError,
|
||||
ID3ParseError,
|
||||
VorbisParseError,
|
||||
MetadataDecodingError,
|
||||
Unauthorized,
|
||||
IncorrectCredentials,
|
||||
}
|
||||
|
||||
impl From<ape::Error> for PError {
|
||||
fn from(_: ape::Error) -> PError {
|
||||
PError::APEParseError
|
||||
PError::MetadataDecodingError
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +42,7 @@ impl From<io::Error> for PError {
|
|||
|
||||
impl From<id3::Error> for PError {
|
||||
fn from(_: id3::Error) -> PError {
|
||||
PError::ID3ParseError
|
||||
PError::MetadataDecodingError
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +54,7 @@ impl From<image::ImageError> for PError {
|
|||
|
||||
impl From<lewton::VorbisError> for PError {
|
||||
fn from(_: lewton::VorbisError) -> PError {
|
||||
PError::VorbisParseError
|
||||
PError::MetadataDecodingError
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,9 +78,7 @@ 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::VorbisParseError => "Error while reading Vorbis tag",
|
||||
PError::MetadataDecodingError => "Error while reading song metadata",
|
||||
PError::Unauthorized => "Authentication required",
|
||||
PError::IncorrectCredentials => "Incorrect username/password combination",
|
||||
}
|
||||
|
@ -118,9 +114,7 @@ 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::VorbisParseError => write!(f, "Error while reading Vorbis tag"),
|
||||
PError::MetadataDecodingError => write!(f, "Error while reading song metadata"),
|
||||
PError::Unauthorized => write!(f, "Authentication required"),
|
||||
PError::IncorrectCredentials => write!(f, "Incorrect username/password combination"),
|
||||
}
|
||||
|
|
132
src/index.rs
132
src/index.rs
|
@ -1,8 +1,4 @@
|
|||
use core::ops::Deref;
|
||||
use ape;
|
||||
use id3::Tag;
|
||||
use lewton::inside_ogg::OggStreamReader;
|
||||
use ogg::PacketReader;
|
||||
use regex::Regex;
|
||||
use sqlite;
|
||||
use sqlite::{Connection, State, Statement, Value};
|
||||
|
@ -13,8 +9,7 @@ use std::thread;
|
|||
use std::time;
|
||||
|
||||
use error::*;
|
||||
use utils;
|
||||
use utils::AudioFormat;
|
||||
use metadata::SongTags;
|
||||
use vfs::Vfs;
|
||||
|
||||
const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 250; // Insertions in each transaction
|
||||
|
@ -41,131 +36,6 @@ pub struct Index {
|
|||
sleep_duration: u64,
|
||||
}
|
||||
|
||||
struct SongTags {
|
||||
disc_number: Option<u32>,
|
||||
track_number: Option<u32>,
|
||||
title: Option<String>,
|
||||
artist: Option<String>,
|
||||
album_artist: Option<String>,
|
||||
album: Option<String>,
|
||||
year: Option<i32>,
|
||||
}
|
||||
|
||||
impl SongTags {
|
||||
fn read(path: &Path) -> Result<SongTags, PError> {
|
||||
match utils::get_audio_format(path) {
|
||||
Some(AudioFormat::MP3) => SongTags::read_id3(path),
|
||||
Some(AudioFormat::MPC) => SongTags::read_ape(path),
|
||||
Some(AudioFormat::OGG) => SongTags::read_vorbis(path),
|
||||
_ => Err(PError::UnsupportedMetadataFormat),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_id3(path: &Path) -> Result<SongTags, PError> {
|
||||
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());
|
||||
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(tag.date_released().and_then(|d| d.year))
|
||||
.or(tag.date_recorded().and_then(|d| d.year));
|
||||
|
||||
Ok(SongTags {
|
||||
artist: artist,
|
||||
album_artist: album_artist,
|
||||
album: album,
|
||||
title: title,
|
||||
disc_number: disc_number,
|
||||
track_number: track_number,
|
||||
year: year,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_ape_string(item: &ape::Item) -> Option<String> {
|
||||
match item.value {
|
||||
ape::ItemValue::Text(ref s) => Some(s.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_ape_i32(item: &ape::Item) -> Option<i32> {
|
||||
match item.value {
|
||||
ape::ItemValue::Text(ref s) => s.parse::<i32>().ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_ape_x_of_y(item: &ape::Item) -> Option<u32> {
|
||||
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<SongTags, PError> {
|
||||
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 disc_number = tag.item("Disc").and_then(SongTags::read_ape_x_of_y);
|
||||
let track_number = tag.item("Track").and_then(SongTags::read_ape_x_of_y);
|
||||
Ok(SongTags {
|
||||
artist: artist,
|
||||
album_artist: album_artist,
|
||||
album: album,
|
||||
title: title,
|
||||
disc_number: disc_number,
|
||||
track_number: track_number,
|
||||
year: year,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_vorbis(path: &Path) -> Result<SongTags, PError> {
|
||||
|
||||
let file = try!(fs::File::open(path));
|
||||
let source = try!(OggStreamReader::new(PacketReader::new(file)));
|
||||
|
||||
let mut tags = SongTags {
|
||||
artist: None,
|
||||
album_artist: None,
|
||||
album: None,
|
||||
title: None,
|
||||
disc_number: None,
|
||||
track_number: None,
|
||||
year: None,
|
||||
};
|
||||
|
||||
for (key, value) in source.comment_hdr.comment_list {
|
||||
match key.as_str() {
|
||||
"TITLE" => tags.title = Some(value),
|
||||
"ALBUM" => tags.album = Some(value),
|
||||
"ARTIST" => tags.artist = Some(value),
|
||||
"ALBUMARTIST" => tags.album_artist = Some(value),
|
||||
"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
|
||||
"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
|
||||
"DATE" => tags.year = value.parse::<i32>().ok(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(tags)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, RustcEncodable)]
|
||||
pub struct Song {
|
||||
path: String,
|
||||
|
|
|
@ -41,6 +41,7 @@ mod config;
|
|||
mod ddns;
|
||||
mod error;
|
||||
mod index;
|
||||
mod metadata;
|
||||
mod ui;
|
||||
mod utils;
|
||||
mod thumbnails;
|
||||
|
|
136
src/metadata.rs
Normal file
136
src/metadata.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
use ape;
|
||||
use id3::Tag;
|
||||
use lewton::inside_ogg::OggStreamReader;
|
||||
use ogg::PacketReader;
|
||||
use regex::Regex;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use error::PError;
|
||||
use utils;
|
||||
use utils::AudioFormat;
|
||||
|
||||
pub struct SongTags {
|
||||
pub disc_number: Option<u32>,
|
||||
pub track_number: Option<u32>,
|
||||
pub title: Option<String>,
|
||||
pub artist: Option<String>,
|
||||
pub album_artist: Option<String>,
|
||||
pub album: Option<String>,
|
||||
pub year: Option<i32>,
|
||||
}
|
||||
|
||||
impl SongTags {
|
||||
pub fn read(path: &Path) -> Result<SongTags, PError> {
|
||||
match utils::get_audio_format(path) {
|
||||
Some(AudioFormat::MP3) => SongTags::read_id3(path),
|
||||
Some(AudioFormat::MPC) => SongTags::read_ape(path),
|
||||
Some(AudioFormat::OGG) => SongTags::read_vorbis(path),
|
||||
_ => Err(PError::UnsupportedMetadataFormat),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_id3(path: &Path) -> Result<SongTags, PError> {
|
||||
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());
|
||||
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(tag.date_released().and_then(|d| d.year))
|
||||
.or(tag.date_recorded().and_then(|d| d.year));
|
||||
|
||||
Ok(SongTags {
|
||||
artist: artist,
|
||||
album_artist: album_artist,
|
||||
album: album,
|
||||
title: title,
|
||||
disc_number: disc_number,
|
||||
track_number: track_number,
|
||||
year: year,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_ape_string(item: &ape::Item) -> Option<String> {
|
||||
match item.value {
|
||||
ape::ItemValue::Text(ref s) => Some(s.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_ape_i32(item: &ape::Item) -> Option<i32> {
|
||||
match item.value {
|
||||
ape::ItemValue::Text(ref s) => s.parse::<i32>().ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_ape_x_of_y(item: &ape::Item) -> Option<u32> {
|
||||
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<SongTags, PError> {
|
||||
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 disc_number = tag.item("Disc").and_then(SongTags::read_ape_x_of_y);
|
||||
let track_number = tag.item("Track").and_then(SongTags::read_ape_x_of_y);
|
||||
Ok(SongTags {
|
||||
artist: artist,
|
||||
album_artist: album_artist,
|
||||
album: album,
|
||||
title: title,
|
||||
disc_number: disc_number,
|
||||
track_number: track_number,
|
||||
year: year,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_vorbis(path: &Path) -> Result<SongTags, PError> {
|
||||
|
||||
let file = try!(fs::File::open(path));
|
||||
let source = try!(OggStreamReader::new(PacketReader::new(file)));
|
||||
|
||||
let mut tags = SongTags {
|
||||
artist: None,
|
||||
album_artist: None,
|
||||
album: None,
|
||||
title: None,
|
||||
disc_number: None,
|
||||
track_number: None,
|
||||
year: None,
|
||||
};
|
||||
|
||||
for (key, value) in source.comment_hdr.comment_list {
|
||||
match key.as_str() {
|
||||
"TITLE" => tags.title = Some(value),
|
||||
"ALBUM" => tags.album = Some(value),
|
||||
"ARTIST" => tags.artist = Some(value),
|
||||
"ALBUMARTIST" => tags.album_artist = Some(value),
|
||||
"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
|
||||
"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
|
||||
"DATE" => tags.year = value.parse::<i32>().ok(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(tags)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue