From 0a7b327c030734f6579d003701c1f8b75e7b0571 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 26 Jun 2021 15:35:39 -0400 Subject: [PATCH] Update tags Signed-off-by: Serial <69764315+Serial-ATA@users.noreply.github.com> --- src/components/tags/ape_tag.rs | 8 +- src/components/tags/id3_tag.rs | 24 ++--- src/components/tags/mp4_tag.rs | 18 ++-- src/components/tags/riff_tag.rs | 12 +-- src/components/tags/vorbis_tag.rs | 160 +++++++++++++++++------------- 5 files changed, 124 insertions(+), 98 deletions(-) diff --git a/src/components/tags/ape_tag.rs b/src/components/tags/ape_tag.rs index 5d5c10fc..c85e052d 100644 --- a/src/components/tags/ape_tag.rs +++ b/src/components/tags/ape_tag.rs @@ -11,7 +11,7 @@ use crate::types::picture::APE_PICTYPES; use ape::Item; use std::borrow::Cow; use std::fs::File; -use std::path::Path; +use std::io::{Read, Seek}; #[impl_tag(ApeInnerTag, TagType::Ape)] pub struct ApeTag; @@ -19,12 +19,12 @@ pub struct ApeTag; impl ApeTag { #[allow(missing_docs)] #[allow(clippy::missing_errors_doc)] - pub fn read_from_path

(path: P) -> Result + pub fn read_from(reader: &mut R) -> Result where - P: AsRef, + R: Read + Seek, { Ok(Self { - inner: ape::read_from_path(&path)?, + inner: ape::read_from(reader)?, #[cfg(feature = "duration")] duration: None, // TODO }) diff --git a/src/components/tags/id3_tag.rs b/src/components/tags/id3_tag.rs index ab256824..de028960 100644 --- a/src/components/tags/id3_tag.rs +++ b/src/components/tags/id3_tag.rs @@ -2,20 +2,18 @@ use crate::tag::Id3Format; use crate::{ - Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error, MimeType, Picture, Result, - TagType, ToAny, ToAnyTag, + Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, LoftyError, MimeType, Picture, + PictureType, Result, TagType, ToAny, ToAnyTag, }; use lofty_attr::impl_tag; pub use id3::Tag as Id3v2InnerTag; -use crate::types::picture::PictureType; use filepath::FilePath; use std::borrow::Cow; use std::convert::{TryFrom, TryInto}; use std::fs::File; use std::io::{Read, Seek, SeekFrom}; -use std::path::Path; #[impl_tag(Id3v2InnerTag, TagType::Id3v2(Id3Format::Default))] pub struct Id3v2Tag; @@ -23,23 +21,23 @@ pub struct Id3v2Tag; impl Id3v2Tag { #[allow(missing_docs)] #[allow(clippy::missing_errors_doc)] - pub fn read_from_path

(path: P, format: &Id3Format) -> Result + pub fn read_from(reader: &mut R, format: &Id3Format) -> Result where - P: AsRef, + R: Read + Seek, { match format { Id3Format::Default => Ok(Self { - inner: Id3v2InnerTag::read_from_path(&path)?, + inner: Id3v2InnerTag::read_from(reader)?, #[cfg(feature = "duration")] - duration: Some(mp3_duration::from_path(&path)?), + duration: None, // TODO }), Id3Format::Riff => Ok(Self { - inner: Id3v2InnerTag::read_from_wav(&path)?, + inner: Id3v2InnerTag::read_from_wav_reader(reader)?, #[cfg(feature = "duration")] duration: None, }), Id3Format::Form => Ok(Self { - inner: Id3v2InnerTag::read_from_aiff(&path)?, + inner: Id3v2InnerTag::read_from_aiff_reader(reader)?, #[cfg(feature = "duration")] duration: None, }), @@ -48,7 +46,8 @@ impl Id3v2Tag { } impl std::convert::TryFrom for Picture { - type Error = Error; + type Error = LoftyError; + fn try_from(inp: id3::frame::Picture) -> Result { let id3::frame::Picture { ref mime_type, @@ -75,7 +74,8 @@ impl std::convert::TryFrom for Picture { } impl TryFrom for id3::frame::Picture { - type Error = Error; + type Error = LoftyError; + fn try_from(inp: Picture) -> Result { Ok(Self { mime_type: String::from(inp.mime_type), diff --git a/src/components/tags/mp4_tag.rs b/src/components/tags/mp4_tag.rs index 632e01a8..f141ac77 100644 --- a/src/components/tags/mp4_tag.rs +++ b/src/components/tags/mp4_tag.rs @@ -1,17 +1,16 @@ #![cfg(feature = "format-mp4")] use crate::{ - Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error, MimeType, Picture, Result, - TagType, ToAny, ToAnyTag, + Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, LoftyError, MimeType, Picture, + PictureType, Result, TagType, ToAny, ToAnyTag, }; use lofty_attr::impl_tag; pub use mp4ameta::{Fourcc, Tag as Mp4InnerTag}; -use crate::types::picture::PictureType; use std::borrow::Cow; use std::fs::File; -use std::path::Path; +use std::io::{Read, Seek}; #[impl_tag(Mp4InnerTag, TagType::Mp4)] pub struct Mp4Tag {} @@ -19,12 +18,12 @@ pub struct Mp4Tag {} impl Mp4Tag { #[allow(missing_docs)] #[allow(clippy::missing_errors_doc)] - pub fn read_from_path

(path: P) -> Result + pub fn read_from(reader: &mut R) -> Result where - P: AsRef, + R: Read + Seek, { Ok(Self { - inner: Mp4InnerTag::read_from_path(path)?, + inner: Mp4InnerTag::read_from(reader)?, #[cfg(feature = "duration")] duration: None, }) @@ -32,7 +31,8 @@ impl Mp4Tag { } impl std::convert::TryFrom for Picture { - type Error = Error; + type Error = LoftyError; + fn try_from(inp: mp4ameta::Data) -> Result { Ok(match inp { mp4ameta::Data::Png(data) => Self { @@ -53,7 +53,7 @@ impl std::convert::TryFrom for Picture { description: None, data: Cow::from(data), }, - _ => return Err(Error::NotAPicture), + _ => return Err(LoftyError::NotAPicture), }) } } diff --git a/src/components/tags/riff_tag.rs b/src/components/tags/riff_tag.rs index f226ee4f..d28ff0fe 100644 --- a/src/components/tags/riff_tag.rs +++ b/src/components/tags/riff_tag.rs @@ -1,6 +1,6 @@ #![cfg(feature = "format-riff")] -use crate::components::logic; +use crate::components::logic::riff; use crate::{ Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Picture, Result, TagType, ToAny, ToAnyTag, }; @@ -9,7 +9,7 @@ use lofty_attr::impl_tag; use std::borrow::Cow; use std::collections::HashMap; use std::fs::File; -use std::path::Path; +use std::io::{Read, Seek}; struct RiffInnerTag { data: Option>, @@ -29,13 +29,13 @@ pub struct RiffTag; impl RiffTag { #[allow(missing_docs)] #[allow(clippy::missing_errors_doc)] - pub fn read_from_path

(path: P) -> Result + pub fn read_from(reader: &mut R) -> Result where - P: AsRef, + R: Read + Seek, { Ok(Self { inner: RiffInnerTag { - data: logic::read::wav(File::open(path)?)?, + data: riff::read_from(reader)?, }, #[cfg(feature = "duration")] duration: None, @@ -235,7 +235,7 @@ impl AudioTagEdit for RiffTag { impl AudioTagWrite for RiffTag { fn write_to(&self, file: &mut File) -> Result<()> { if let Some(data) = self.inner.data.clone() { - crate::components::logic::write::riff(file, data)?; + riff::write_to(file, data)?; } Ok(()) diff --git a/src/components/tags/vorbis_tag.rs b/src/components/tags/vorbis_tag.rs index 3450fb61..57222d25 100644 --- a/src/components/tags/vorbis_tag.rs +++ b/src/components/tags/vorbis_tag.rs @@ -4,26 +4,29 @@ feature = "format-flac" ))] -use crate::components::logic::write::vorbis_generic; -use crate::{ - Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error, Picture, PictureType, Result, - TagType, ToAny, ToAnyTag, VorbisFormat, +use crate::components::logic::constants::{ + OPUSHEAD, OPUSTAGS, VORBIS_COMMENT_HEAD, VORBIS_IDENT_HEAD, }; +use crate::components::logic::{flac, ogg_generic}; +use crate::{ + Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, LoftyError, OggFormat, Picture, + PictureType, Result, TagType, ToAny, ToAnyTag, +}; + +#[cfg(feature = "format-opus")] +use crate::components::logic::ogg_generic::OGGTags; + use lofty_attr::impl_tag; use lewton::inside_ogg::OggStreamReader; -use opus_headers::OpusHeaders; use std::borrow::Cow; use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::fs::File; -use std::path::Path; - -pub const VORBIS: [u8; 7] = [3, 118, 111, 114, 98, 105, 115]; -const OPUSTAGS: [u8; 8] = [79, 112, 117, 115, 84, 97, 103, 115]; +use std::io::{Read, Seek}; struct VorbisInnerTag { - format: Option, + format: Option, vendor: String, comments: HashMap, pictures: Option>, @@ -66,25 +69,25 @@ impl VorbisInnerTag { self.comments = comments; } - fn from_path

(path: P, format: &VorbisFormat) -> Result + fn read_from(reader: &mut R, format: &OggFormat) -> Result where - P: AsRef, + R: Read + Seek, { match format { - VorbisFormat::Ogg => { - let tag = lewton::inside_ogg::OggStreamReader::new(File::open(path)?)?; + OggFormat::Vorbis => { + let tag = ogg_generic::read_from(reader, &VORBIS_IDENT_HEAD, &VORBIS_COMMENT_HEAD)?; let vorbis_tag: VorbisTag = tag.try_into()?; Ok(vorbis_tag.inner) }, - VorbisFormat::Opus => { - let tag = opus_headers::parse_from_path(path)?; + OggFormat::Opus => { + let tag = ogg_generic::read_from(reader, &OPUSHEAD, &OPUSTAGS)?; let vorbis_tag: VorbisTag = tag.try_into()?; Ok(vorbis_tag.inner) }, - VorbisFormat::Flac => { - let tag = metaflac::Tag::read_from_path(path)?; + OggFormat::Flac => { + let tag = metaflac::Tag::read_from(reader)?; let vorbis_tag: VorbisTag = tag.try_into()?; Ok(vorbis_tag.inner) @@ -93,12 +96,12 @@ impl VorbisInnerTag { } } -#[impl_tag(VorbisInnerTag, TagType::Vorbis(VorbisFormat::Ogg))] +#[impl_tag(VorbisInnerTag, TagType::Ogg(OggFormat::Vorbis))] pub struct VorbisTag; #[cfg(feature = "format-vorbis")] impl TryFrom> for VorbisTag { - type Error = crate::Error; + type Error = LoftyError; fn try_from(inp: OggStreamReader) -> Result { let mut tag = Self::default(); @@ -118,7 +121,7 @@ impl TryFrom> for VorbisTag { } tag.inner = VorbisInnerTag { - format: Some(VorbisFormat::Ogg), + format: Some(OggFormat::Vorbis), vendor: inp.comment_hdr.vendor, comments: comments.into_iter().collect(), pictures: if pictures.is_empty() { @@ -133,28 +136,34 @@ impl TryFrom> for VorbisTag { } #[cfg(feature = "format-opus")] -impl TryFrom for VorbisTag { - type Error = crate::Error; +impl TryFrom for VorbisTag { + type Error = LoftyError; - fn try_from(inp: OpusHeaders) -> Result { + fn try_from(inp: OGGTags) -> Result { let mut tag = Self::default(); - let mut comments = inp.comments.user_comments; + let read_pictures = inp.1; + let comments = inp.2; - // TODO: opus_headers doesn't store all keys - let mut pictures = None; + let mut pictures = Vec::new(); - if let Some(data) = comments.remove("METADATA_BLOCK_PICTURE") { - if let Ok(pic) = Picture::from_apic_bytes(&data.as_bytes()) { - pictures = Some(Cow::from(vec![pic])) + if !read_pictures.is_empty() { + for pic in read_pictures { + if let Ok(pic) = Picture::from_apic_bytes(&pic.as_bytes()) { + pictures.push(pic) + } } } tag.inner = VorbisInnerTag { - format: Some(VorbisFormat::Opus), - vendor: inp.comments.vendor, - comments, - pictures, + format: Some(inp.3), + vendor: inp.0, + comments: comments.into_iter().collect(), + pictures: if pictures.is_empty() { + None + } else { + Some(Cow::from(pictures)) + }, }; Ok(tag) @@ -163,7 +172,7 @@ impl TryFrom for VorbisTag { #[cfg(feature = "format-flac")] impl TryFrom for VorbisTag { - type Error = crate::Error; + type Error = LoftyError; fn try_from(inp: metaflac::Tag) -> Result { let mut tag = Self::default(); @@ -194,7 +203,7 @@ impl TryFrom for VorbisTag { comment_collection.into_iter().collect(); tag.inner = VorbisInnerTag { - format: Some(VorbisFormat::Flac), + format: Some(OggFormat::Flac), vendor: comments.vendor_string, comments: comment_collection, pictures: Some(Cow::from(pictures)), @@ -203,19 +212,19 @@ impl TryFrom for VorbisTag { return Ok(tag); } - Err(Error::InvalidData) + Err(LoftyError::InvalidData("Flac file contains invalid data")) } } impl VorbisTag { #[allow(missing_docs)] #[allow(clippy::missing_errors_doc)] - pub fn read_from_path

(path: P, format: &VorbisFormat) -> Result + pub fn read_from(reader: &mut R, format: &OggFormat) -> Result where - P: AsRef, + R: Read + Seek, { Ok(Self { - inner: VorbisInnerTag::from_path(path, &format)?, + inner: VorbisInnerTag::read_from(reader, format)?, #[cfg(feature = "duration")] duration: None, }) @@ -314,10 +323,10 @@ impl AudioTagEdit for VorbisTag { } fn set_front_cover(&mut self, cover: Picture) { - self.remove_front_cover(); - - let pictures = create_cover(&cover, &self.inner.pictures); - self.inner.pictures = pictures + if let Some(pic) = create_cover(&cover) { + self.remove_front_cover(); + self.inner.pictures = Some(replace_pic(pic, &self.inner.pictures)) + } } fn remove_front_cover(&mut self) { @@ -333,10 +342,10 @@ impl AudioTagEdit for VorbisTag { } fn set_back_cover(&mut self, cover: Picture) { - self.remove_front_cover(); - - let pictures = create_cover(&cover, &self.inner.pictures); - self.inner.pictures = pictures + if let Some(pic) = create_cover(&cover) { + self.remove_back_cover(); + self.inner.pictures = Some(replace_pic(pic, &self.inner.pictures)) + } } fn remove_back_cover(&mut self) { @@ -425,39 +434,56 @@ fn get_cover(p_type: PictureType, pictures: &Option>) -> } } -fn create_cover( - cover: &Picture, - pictures: &Option>, -) -> Option> { +fn create_cover(cover: &Picture) -> Option { if cover.pic_type == PictureType::CoverFront || cover.pic_type == PictureType::CoverBack { if let Ok(pic) = Picture::from_apic_bytes(&cover.as_apic_bytes()) { - if let Some(pictures) = pictures { - let mut pictures = pictures.to_vec(); - pictures.retain(|p| p.pic_type != PictureType::CoverBack); - - pictures.push(pic); - return Some(Cow::from(pictures)); - } - - return Some(Cow::from(vec![pic])); + return Some(pic); } } None } +fn replace_pic( + pic: Picture, + pictures: &Option>, +) -> Cow<'static, [Picture]> { + if let Some(pictures) = pictures { + let mut pictures = pictures.to_vec(); + pictures.retain(|p| p.pic_type != pic.pic_type); + + pictures.push(pic); + + Cow::from(pictures) + } else { + Cow::from(vec![pic]) + } +} + impl AudioTagWrite for VorbisTag { fn write_to(&self, file: &mut File) -> Result<()> { if let Some(format) = self.inner.format.clone() { match format { - VorbisFormat::Ogg => { - vorbis_generic(file, &VORBIS, &self.inner.vendor, &self.inner.comments)?; + OggFormat::Vorbis => { + ogg_generic::create_pages( + file, + &VORBIS_COMMENT_HEAD, + &self.inner.vendor, + &self.inner.comments, + &self.inner.pictures, + )?; }, - VorbisFormat::Opus => { - vorbis_generic(file, &OPUSTAGS, &self.inner.vendor, &self.inner.comments)?; + OggFormat::Opus => { + ogg_generic::create_pages( + file, + &OPUSTAGS, + &self.inner.vendor, + &self.inner.comments, + &self.inner.pictures, + )?; }, - VorbisFormat::Flac => { - crate::components::logic::write::flac( + OggFormat::Flac => { + flac::write_to( file, &self.inner.vendor, &self.inner.comments,