mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-13 14:12:31 +00:00
Fix writing to flac + some cleanup
This commit is contained in:
parent
0d5724d661
commit
63a92c87ef
4 changed files with 125 additions and 94 deletions
|
@ -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<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>>
|
||||
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<u8>,
|
||||
|
@ -116,6 +181,7 @@ struct Page {
|
|||
pub end: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "format-opus")]
|
||||
pub(crate) fn opus<T>(mut data: T, packet: &[u8]) -> Result<Vec<u8>>
|
||||
where
|
||||
T: Read + Seek,
|
||||
|
@ -214,11 +280,45 @@ where
|
|||
Ok(content)
|
||||
}
|
||||
|
||||
pub(crate) fn wav<T>(mut data: T, packet: Vec<u8>) -> Result<Vec<u8>>
|
||||
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<String, String>) -> 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<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 {
|
||||
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(),
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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<metaflac::Block> =
|
||||
tag.blocks().map(std::borrow::ToOwned::to_owned).collect();
|
||||
|
||||
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)?;
|
||||
}
|
||||
crate::components::logic::write::flac(
|
||||
file,
|
||||
&self.inner.vendor,
|
||||
&self.inner.comments,
|
||||
&self.inner.pictures,
|
||||
)?;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]),
|
||||
},
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue