From c3e5e27f231e7b1875f22fb76cffc5c28d1c23e5 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Wed, 16 Nov 2016 16:51:09 -0800 Subject: [PATCH] Moved metadata decoding to a separate module --- src/api.rs | 4 +- src/error.rs | 18 +++---- src/index.rs | 132 +--------------------------------------------- src/main.rs | 1 + src/metadata.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 146 deletions(-) create mode 100644 src/metadata.rs diff --git a/src/api.rs b/src/api.rs index a1076f0..3d8ba75 100644 --- a/src/api.rs +++ b/src/api.rs @@ -39,9 +39,7 @@ 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::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), } diff --git a/src/error.rs b/src/error.rs index 50cc3ea..93c35e9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,16 +23,14 @@ pub enum PError { AlbumArtSearchError, ImageProcessingError, UnsupportedMetadataFormat, - APEParseError, - ID3ParseError, - VorbisParseError, + MetadataDecodingError, Unauthorized, IncorrectCredentials, } impl From for PError { fn from(_: ape::Error) -> PError { - PError::APEParseError + PError::MetadataDecodingError } } @@ -44,7 +42,7 @@ impl From for PError { impl From for PError { fn from(_: id3::Error) -> PError { - PError::ID3ParseError + PError::MetadataDecodingError } } @@ -56,7 +54,7 @@ impl From for PError { impl From 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"), } diff --git a/src/index.rs b/src/index.rs index ea6bf5d..33e06dd 100644 --- a/src/index.rs +++ b/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, - track_number: Option, - title: Option, - artist: Option, - album_artist: Option, - album: Option, - year: Option, -} - -impl SongTags { - fn read(path: &Path) -> Result { - 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 { - 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 { - 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_x_of_y(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 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 { - - 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::().ok(), - "DISCNUMBER" => tags.disc_number = value.parse::().ok(), - "DATE" => tags.year = value.parse::().ok(), - _ => (), - } - } - - Ok(tags) - } -} - #[derive(Debug, RustcEncodable)] pub struct Song { path: String, diff --git a/src/main.rs b/src/main.rs index 271f181..e5f2f24 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,6 +41,7 @@ mod config; mod ddns; mod error; mod index; +mod metadata; mod ui; mod utils; mod thumbnails; diff --git a/src/metadata.rs b/src/metadata.rs new file mode 100644 index 0000000..1759b5d --- /dev/null +++ b/src/metadata.rs @@ -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, + pub track_number: Option, + pub title: Option, + pub artist: Option, + pub album_artist: Option, + pub album: Option, + pub year: Option, +} + +impl SongTags { + pub fn read(path: &Path) -> Result { + 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 { + 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 { + 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_x_of_y(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 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 { + + 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::().ok(), + "DISCNUMBER" => tags.disc_number = value.parse::().ok(), + "DATE" => tags.year = value.parse::().ok(), + _ => (), + } + } + + Ok(tags) + } +} \ No newline at end of file