Fix writing to flac + some cleanup

This commit is contained in:
Serial 2021-05-19 15:01:44 -04:00
parent 0d5724d661
commit 63a92c87ef
4 changed files with 125 additions and 94 deletions

View file

@ -1,12 +1,16 @@
use crate::vorbis_tag::VORBIS; 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 ogg::PacketWriteEndInfo;
use std::borrow::{BorrowMut, Cow};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io::{Cursor, Read, Seek, SeekFrom, Write}; use std::io::{Cursor, Read, Seek, SeekFrom, Write};
#[cfg(any(feature = "format-vorbis", feature = "format-opus"))]
pub(crate) fn vorbis_generic( pub(crate) fn vorbis_generic(
file: &mut File, file: &mut File,
sig: &[u8], sig: &[u8],
@ -58,6 +62,66 @@ pub(crate) fn vorbis_generic(
Ok(()) Ok(())
} }
#[cfg(feature = "format-flac")]
pub(crate) fn flac<T>(
mut data: T,
vendor: &str,
comments: &HashMap<String, String>,
pictures: &Option<Cow<'static, [Picture]>>,
) -> 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<metaflac::Block> = Vec::new();
let mut comment_collection: HashMap<String, Vec<String>> = 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<T>(data: T, packet: &[u8]) -> Result<Vec<u8>> pub(crate) fn ogg<T>(data: T, packet: &[u8]) -> Result<Vec<u8>>
where where
T: Read + Seek, T: Read + Seek,
@ -108,6 +172,7 @@ where
Ok(c.into_inner()) Ok(c.into_inner())
} }
#[cfg(feature = "format-opus")]
struct Page { struct Page {
pub size_idx: usize, pub size_idx: usize,
pub content: Vec<u8>, pub content: Vec<u8>,
@ -116,6 +181,7 @@ struct Page {
pub end: usize, pub end: usize,
} }
#[cfg(feature = "format-opus")]
pub(crate) fn opus<T>(mut data: T, packet: &[u8]) -> Result<Vec<u8>> pub(crate) fn opus<T>(mut data: T, packet: &[u8]) -> Result<Vec<u8>>
where where
T: Read + Seek, T: Read + Seek,
@ -214,11 +280,45 @@ where
Ok(content) Ok(content)
} }
pub(crate) fn wav<T>(mut data: T, packet: Vec<u8>) -> Result<Vec<u8>> #[cfg(feature = "format-riff")]
where pub(crate) fn riff(data: &mut File, metadata: HashMap<String, String>) -> Result<()> {
T: Read + Seek + Write, let mut packet = Vec::new();
{
let chunk = riff::Chunk::read(&mut data, 0)?; 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<u32>, Option<u32>) = (None, None); let (mut list_pos, mut list_len): (Option<u32>, Option<u32>) = (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 { if child.id() == riff::LIST_ID {
list_pos = Some(child.offset() as u32); list_pos = Some(child.offset() as u32);
list_len = Some(child.len()); list_len = Some(child.len());
} }
} }
data.seek(SeekFrom::Start(0))?; file.seek(SeekFrom::Start(0))?;
let mut content = Vec::new(); 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) { if let (Some(list_pos), Some(list_len)) = (list_pos, list_len) {
let list_end = (list_pos + list_len) as usize; let list_end = (list_pos + list_len) as usize;
@ -248,7 +348,11 @@ where
let total_size = (content.len() - 8) as u32; let total_size = (content.len() - 8) as u32;
let _ = content.splice(4..8, total_size.to_le_bytes().to_vec()); 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 { } else {
Err(Error::Wav( Err(Error::Wav(
"This file does not contain an INFO chunk".to_string(), "This file does not contain an INFO chunk".to_string(),

View file

@ -6,10 +6,9 @@ use crate::{
}; };
use lofty_attr::impl_tag; use lofty_attr::impl_tag;
use std::borrow::{BorrowMut, Cow}; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io::{Cursor, Seek, SeekFrom, Write};
use std::path::Path; use std::path::Path;
struct RiffInnerTag { struct RiffInnerTag {
@ -236,45 +235,7 @@ impl AudioTagEdit for RiffTag {
impl AudioTagWrite for RiffTag { impl AudioTagWrite for RiffTag {
fn write_to(&self, file: &mut File) -> Result<()> { fn write_to(&self, file: &mut File) -> Result<()> {
if let Some(data) = self.inner.data.clone() { if let Some(data) = self.inner.data.clone() {
let mut chunk = Vec::new(); crate::components::logic::write::riff(file, data)?;
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)?;
} }
Ok(()) Ok(())

View file

@ -17,7 +17,6 @@ use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::fs::File; use std::fs::File;
use std::io::Write;
use std::path::Path; use std::path::Path;
pub const VORBIS: [u8; 7] = [3, 118, 111, 114, 98, 105, 115]; 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)?; vorbis_generic(file, &OPUSTAGS, &self.inner.vendor, &self.inner.comments)?;
}, },
VorbisFormat::Flac => { VorbisFormat::Flac => {
// TODO crate::components::logic::write::flac(
let tag = metaflac::Tag::read_from(file)?; file,
&self.inner.vendor,
let mut blocks: Vec<metaflac::Block> = &self.inner.comments,
tag.blocks().map(std::borrow::ToOwned::to_owned).collect(); &self.inner.pictures,
)?;
let mut pictures: Vec<metaflac::Block> = Vec::new();
let mut comment_collection: HashMap<String, Vec<String>> = 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)?;
}
}, },
} }
} }

View file

@ -41,17 +41,13 @@ macro_rules! add_tags {
pic_type: PictureType::CoverFront, pic_type: PictureType::CoverFront,
mime_type: MimeType::Jpeg, mime_type: MimeType::Jpeg,
description: Some(Cow::from("test")), description: Some(Cow::from("test")),
data: Cow::from(vec![ data: Cow::from(vec![0; 20]),
51, 50, 51, 50, 51, 50, 51, 50, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
]),
}, },
Picture { Picture {
pic_type: PictureType::CoverBack, pic_type: PictureType::CoverBack,
mime_type: MimeType::Jpeg, mime_type: MimeType::Jpeg,
description: Some(Cow::from("test")), description: Some(Cow::from("test")),
data: Cow::from(vec![ data: Cow::from(vec![0; 20]),
51, 50, 51, 50, 51, 50, 51, 50, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
]),
}, },
); );