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::AlbumArtSearchError => IronError::new(err, status::InternalServerError),
|
||||||
PError::ImageProcessingError => IronError::new(err, status::InternalServerError),
|
PError::ImageProcessingError => IronError::new(err, status::InternalServerError),
|
||||||
PError::UnsupportedMetadataFormat => IronError::new(err, status::InternalServerError),
|
PError::UnsupportedMetadataFormat => IronError::new(err, status::InternalServerError),
|
||||||
PError::APEParseError => IronError::new(err, status::InternalServerError),
|
PError::MetadataDecodingError => IronError::new(err, status::InternalServerError),
|
||||||
PError::ID3ParseError => IronError::new(err, status::InternalServerError),
|
|
||||||
PError::VorbisParseError => IronError::new(err, status::InternalServerError),
|
|
||||||
PError::Unauthorized => IronError::new(err, status::Unauthorized),
|
PError::Unauthorized => IronError::new(err, status::Unauthorized),
|
||||||
PError::IncorrectCredentials => IronError::new(err, status::BadRequest),
|
PError::IncorrectCredentials => IronError::new(err, status::BadRequest),
|
||||||
}
|
}
|
||||||
|
|
18
src/error.rs
18
src/error.rs
|
@ -23,16 +23,14 @@ pub enum PError {
|
||||||
AlbumArtSearchError,
|
AlbumArtSearchError,
|
||||||
ImageProcessingError,
|
ImageProcessingError,
|
||||||
UnsupportedMetadataFormat,
|
UnsupportedMetadataFormat,
|
||||||
APEParseError,
|
MetadataDecodingError,
|
||||||
ID3ParseError,
|
|
||||||
VorbisParseError,
|
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
IncorrectCredentials,
|
IncorrectCredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ape::Error> for PError {
|
impl From<ape::Error> for PError {
|
||||||
fn from(_: ape::Error) -> 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 {
|
impl From<id3::Error> for PError {
|
||||||
fn from(_: id3::Error) -> 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 {
|
impl From<lewton::VorbisError> for PError {
|
||||||
fn from(_: lewton::VorbisError) -> 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::AlbumArtSearchError => "Error while looking for album art",
|
||||||
PError::ImageProcessingError => "Error while processing image",
|
PError::ImageProcessingError => "Error while processing image",
|
||||||
PError::UnsupportedMetadataFormat => "Unsupported metadata format",
|
PError::UnsupportedMetadataFormat => "Unsupported metadata format",
|
||||||
PError::APEParseError => "Error while reading APE tag",
|
PError::MetadataDecodingError => "Error while reading song metadata",
|
||||||
PError::ID3ParseError => "Error while reading ID3 tag",
|
|
||||||
PError::VorbisParseError => "Error while reading Vorbis tag",
|
|
||||||
PError::Unauthorized => "Authentication required",
|
PError::Unauthorized => "Authentication required",
|
||||||
PError::IncorrectCredentials => "Incorrect username/password combination",
|
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::AlbumArtSearchError => write!(f, "Error while looking for album art"),
|
||||||
PError::ImageProcessingError => write!(f, "Error while processing image"),
|
PError::ImageProcessingError => write!(f, "Error while processing image"),
|
||||||
PError::UnsupportedMetadataFormat => write!(f, "Unsupported metadata format"),
|
PError::UnsupportedMetadataFormat => write!(f, "Unsupported metadata format"),
|
||||||
PError::APEParseError => write!(f, "Error while reading APE tag"),
|
PError::MetadataDecodingError => write!(f, "Error while reading song metadata"),
|
||||||
PError::ID3ParseError => write!(f, "Error while reading ID3 tag"),
|
|
||||||
PError::VorbisParseError => write!(f, "Error while reading Vorbis tag"),
|
|
||||||
PError::Unauthorized => write!(f, "Authentication required"),
|
PError::Unauthorized => write!(f, "Authentication required"),
|
||||||
PError::IncorrectCredentials => write!(f, "Incorrect username/password combination"),
|
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 core::ops::Deref;
|
||||||
use ape;
|
|
||||||
use id3::Tag;
|
|
||||||
use lewton::inside_ogg::OggStreamReader;
|
|
||||||
use ogg::PacketReader;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use sqlite;
|
use sqlite;
|
||||||
use sqlite::{Connection, State, Statement, Value};
|
use sqlite::{Connection, State, Statement, Value};
|
||||||
|
@ -13,8 +9,7 @@ use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
use error::*;
|
use error::*;
|
||||||
use utils;
|
use metadata::SongTags;
|
||||||
use utils::AudioFormat;
|
|
||||||
use vfs::Vfs;
|
use vfs::Vfs;
|
||||||
|
|
||||||
const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 250; // Insertions in each transaction
|
const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 250; // Insertions in each transaction
|
||||||
|
@ -41,131 +36,6 @@ pub struct Index {
|
||||||
sleep_duration: u64,
|
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)]
|
#[derive(Debug, RustcEncodable)]
|
||||||
pub struct Song {
|
pub struct Song {
|
||||||
path: String,
|
path: String,
|
||||||
|
|
|
@ -41,6 +41,7 @@ mod config;
|
||||||
mod ddns;
|
mod ddns;
|
||||||
mod error;
|
mod error;
|
||||||
mod index;
|
mod index;
|
||||||
|
mod metadata;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod thumbnails;
|
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