diff --git a/src/components/logic/write.rs b/src/components/logic/write.rs index b43d64ac..0e90a545 100644 --- a/src/components/logic/write.rs +++ b/src/components/logic/write.rs @@ -1,12 +1,16 @@ use crate::vorbis_tag::VORBIS; -use crate::{Error, Result}; +use crate::{Error, Picture, Result}; -#[cfg(feature = "ogg")] +#[cfg(feature = "format-flac")] +use metaflac::BlockType; +#[cfg(feature = "format-vorbis")] use ogg::PacketWriteEndInfo; +use std::borrow::{BorrowMut, Cow}; use std::collections::HashMap; use std::fs::File; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; +#[cfg(any(feature = "format-vorbis", feature = "format-opus"))] pub(crate) fn vorbis_generic( file: &mut File, sig: &[u8], @@ -58,6 +62,66 @@ pub(crate) fn vorbis_generic( Ok(()) } +#[cfg(feature = "format-flac")] +pub(crate) fn flac( + mut data: T, + vendor: &str, + comments: &HashMap, + pictures: &Option>, +) -> Result<()> +where + T: Read + Write + Seek, +{ + let mut tag = metaflac::Tag::read_from(&mut data)?; + + tag.remove_blocks(BlockType::VorbisComment); + tag.remove_blocks(BlockType::Picture); + + let mut padding = None; + + if let Some(pad) = tag.get_blocks(BlockType::Padding).last() { + padding = Some(pad.clone()); + tag.remove_blocks(BlockType::Padding) + } + + let mut pics_final: Vec = Vec::new(); + let mut comment_collection: HashMap> = HashMap::new(); + + if let Some(pics) = pictures.clone() { + for pic in pics.iter() { + pics_final.push(metaflac::Block::Picture( + metaflac::block::Picture::from_bytes(&*pic.as_apic_bytes())?, + )) + } + } + + for (k, v) in comments.clone() { + comment_collection.insert(k, vec![v]); + } + + let comments = metaflac::Block::VorbisComment(metaflac::block::VorbisComment { + vendor_string: vendor.to_string(), + comments: comment_collection, + }); + + tag.push_block(comments); + + for pic in pics_final { + tag.push_block(pic) + } + + if let Some(padding) = padding { + tag.push_block(padding) + } + + data.seek(SeekFrom::Start(0))?; + + tag.write_to(&mut data)?; + + Ok(()) +} + +#[cfg(feature = "format-vorbis")] pub(crate) fn ogg(data: T, packet: &[u8]) -> Result> where T: Read + Seek, @@ -108,6 +172,7 @@ where Ok(c.into_inner()) } +#[cfg(feature = "format-opus")] struct Page { pub size_idx: usize, pub content: Vec, @@ -116,6 +181,7 @@ struct Page { pub end: usize, } +#[cfg(feature = "format-opus")] pub(crate) fn opus(mut data: T, packet: &[u8]) -> Result> where T: Read + Seek, @@ -214,11 +280,45 @@ where Ok(content) } -pub(crate) fn wav(mut data: T, packet: Vec) -> Result> -where - T: Read + Seek + Write, -{ - let chunk = riff::Chunk::read(&mut data, 0)?; +#[cfg(feature = "format-riff")] +pub(crate) fn riff(data: &mut File, metadata: HashMap) -> Result<()> { + let mut packet = Vec::new(); + + packet.extend(riff::LIST_ID.value.iter()); + + let fourcc = "INFO"; + packet.extend(fourcc.as_bytes().iter()); + + for (k, v) in metadata { + if let Some(fcc) = super::read::key_to_fourcc(&*k) { + let mut val = v.as_bytes().to_vec(); + + if val.len() % 2 != 0 { + val.push(0) + } + + let size = val.len() as u32; + + packet.extend(fcc.iter()); + packet.extend(size.to_le_bytes().iter()); + packet.extend(val.iter()); + } + } + + let mut file_bytes = Vec::new(); + std::io::copy(data.borrow_mut(), &mut file_bytes)?; + + let len = (packet.len() - 4) as u32; + let size = len.to_le_bytes(); + + #[allow(clippy::needless_range_loop)] + for i in 0..4 { + packet.insert(i + 4, size[i]); + } + + let mut file = Cursor::new(file_bytes); + + let chunk = riff::Chunk::read(&mut file, 0)?; let (mut list_pos, mut list_len): (Option, Option) = (None, None); @@ -228,17 +328,17 @@ where )); } - for child in chunk.iter(&mut data) { + for child in chunk.iter(&mut file) { if child.id() == riff::LIST_ID { list_pos = Some(child.offset() as u32); list_len = Some(child.len()); } } - data.seek(SeekFrom::Start(0))?; + file.seek(SeekFrom::Start(0))?; let mut content = Vec::new(); - std::io::copy(&mut data, &mut content)?; + std::io::copy(&mut file, &mut content)?; if let (Some(list_pos), Some(list_len)) = (list_pos, list_len) { let list_end = (list_pos + list_len) as usize; @@ -248,7 +348,11 @@ where let total_size = (content.len() - 8) as u32; let _ = content.splice(4..8, total_size.to_le_bytes().to_vec()); - Ok(content) + data.seek(SeekFrom::Start(0))?; + data.set_len(0)?; + data.write_all(&*content)?; + + Ok(()) } else { Err(Error::Wav( "This file does not contain an INFO chunk".to_string(), diff --git a/src/components/tags/riff_tag.rs b/src/components/tags/riff_tag.rs index a6701fe8..f226ee4f 100644 --- a/src/components/tags/riff_tag.rs +++ b/src/components/tags/riff_tag.rs @@ -6,10 +6,9 @@ use crate::{ }; use lofty_attr::impl_tag; -use std::borrow::{BorrowMut, Cow}; +use std::borrow::Cow; use std::collections::HashMap; use std::fs::File; -use std::io::{Cursor, Seek, SeekFrom, Write}; use std::path::Path; struct RiffInnerTag { @@ -236,45 +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() { - let mut chunk = Vec::new(); - - chunk.extend(riff::LIST_ID.value.iter()); - - let fourcc = "INFO"; - chunk.extend(fourcc.as_bytes().iter()); - - for (k, v) in data { - if let Some(fcc) = logic::read::key_to_fourcc(&*k) { - let mut val = v.as_bytes().to_vec(); - - if val.len() % 2 != 0 { - val.push(0) - } - - let size = val.len() as u32; - - chunk.extend(fcc.iter()); - chunk.extend(size.to_le_bytes().iter()); - chunk.extend(val.iter()); - } - } - - let mut file_bytes = Vec::new(); - std::io::copy(file.borrow_mut(), &mut file_bytes)?; - - let len = (chunk.len() - 4) as u32; - let size = len.to_le_bytes(); - - #[allow(clippy::needless_range_loop)] - for i in 0..4 { - chunk.insert(i + 4, size[i]); - } - - let data = logic::write::wav(Cursor::new(file_bytes), chunk)?; - - file.seek(SeekFrom::Start(0))?; - file.set_len(0)?; - file.write_all(&*data)?; + crate::components::logic::write::riff(file, data)?; } Ok(()) diff --git a/src/components/tags/vorbis_tag.rs b/src/components/tags/vorbis_tag.rs index 82fa31d0..3450fb61 100644 --- a/src/components/tags/vorbis_tag.rs +++ b/src/components/tags/vorbis_tag.rs @@ -17,7 +17,6 @@ use std::borrow::Cow; use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::fs::File; -use std::io::Write; use std::path::Path; pub const VORBIS: [u8; 7] = [3, 118, 111, 114, 98, 105, 115]; @@ -458,41 +457,12 @@ impl AudioTagWrite for VorbisTag { vorbis_generic(file, &OPUSTAGS, &self.inner.vendor, &self.inner.comments)?; }, VorbisFormat::Flac => { - // TODO - let tag = metaflac::Tag::read_from(file)?; - - let mut blocks: Vec = - tag.blocks().map(std::borrow::ToOwned::to_owned).collect(); - - let mut pictures: Vec = Vec::new(); - let mut comment_collection: HashMap> = HashMap::new(); - - if let Some(pics) = self.inner.pictures.clone() { - for pic in pics.iter() { - pictures.push(metaflac::Block::Picture( - metaflac::block::Picture::from_bytes(&*pic.as_apic_bytes())?, - )) - } - } - - for (k, v) in self.inner.comments.clone() { - comment_collection.insert(k, vec![v]); - } - - blocks[1] = metaflac::Block::VorbisComment(metaflac::block::VorbisComment { - vendor_string: self.inner.vendor.clone(), - comments: comment_collection, - }); - - blocks.append(&mut pictures); - - file.write_all(b"fLaC")?; - - let total = blocks.len(); - - for (i, block) in blocks.iter().enumerate() { - block.write_to(i == total - 1, file)?; - } + crate::components::logic::write::flac( + file, + &self.inner.vendor, + &self.inner.comments, + &self.inner.pictures, + )?; }, } } diff --git a/tests/io.rs b/tests/io.rs index 49e2bbf2..6b32997b 100644 --- a/tests/io.rs +++ b/tests/io.rs @@ -41,17 +41,13 @@ macro_rules! add_tags { pic_type: PictureType::CoverFront, mime_type: MimeType::Jpeg, description: Some(Cow::from("test")), - data: Cow::from(vec![ - 51, 50, 51, 50, 51, 50, 51, 50, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - ]), + data: Cow::from(vec![0; 20]), }, Picture { pic_type: PictureType::CoverBack, mime_type: MimeType::Jpeg, description: Some(Cow::from("test")), - data: Cow::from(vec![ - 51, 50, 51, 50, 51, 50, 51, 50, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - ]), + data: Cow::from(vec![0; 20]), }, );