mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-13 22:22:31 +00:00
Cleanup AIFF text chunk read/write
This commit is contained in:
parent
549a5d4730
commit
d94816e439
5 changed files with 65 additions and 150 deletions
|
@ -1,11 +1,11 @@
|
|||
use crate::{FileProperties, LoftyError, Result};
|
||||
|
||||
use std::cmp::{max, min};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::time::Duration;
|
||||
|
||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn verify_aiff<T>(data: &mut T) -> Result<()>
|
||||
where
|
||||
|
@ -132,20 +132,11 @@ where
|
|||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "format-aiff")] {
|
||||
type AiffTags = (
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
FileProperties,
|
||||
);
|
||||
|
||||
pub(crate) fn read_from<T>(data: &mut T) -> Result<AiffTags>
|
||||
pub(crate) fn read_from<T>(data: &mut T) -> Result<(HashMap<String, String>, FileProperties)>
|
||||
where
|
||||
T: Read + Seek,
|
||||
{
|
||||
let mut name_id: Option<String> = None;
|
||||
let mut author_id: Option<String> = None;
|
||||
let mut copyright_id: Option<String> = None;
|
||||
let mut metadata = HashMap::<String, String>::new();
|
||||
|
||||
let properties = read_properties(data)?;
|
||||
|
||||
|
@ -153,84 +144,49 @@ cfg_if::cfg_if! {
|
|||
data.read_u32::<LittleEndian>(),
|
||||
data.read_u32::<BigEndian>(),
|
||||
) {
|
||||
match &fourcc.to_le_bytes() {
|
||||
f if f == b"NAME" && name_id.is_none() => {
|
||||
let mut name = vec![0; size as usize];
|
||||
data.read_exact(&mut name)?;
|
||||
let fourcc_b = &fourcc.to_le_bytes();
|
||||
|
||||
name_id = Some(String::from_utf8(name)?);
|
||||
},
|
||||
f if f == b"AUTH" && author_id.is_none() => {
|
||||
let mut auth = vec![0; size as usize];
|
||||
data.read_exact(&mut auth)?;
|
||||
if fourcc_b == b"NAME" || fourcc_b == b"AUTH" || fourcc_b == b"(c) " {
|
||||
let mut value = vec![0; size as usize];
|
||||
data.read_exact(&mut value)?;
|
||||
|
||||
author_id = Some(String::from_utf8(auth)?);
|
||||
},
|
||||
f if f == b"(c) " && copyright_id.is_none() => {
|
||||
let mut copy = vec![0; size as usize];
|
||||
data.read_exact(&mut copy)?;
|
||||
|
||||
copyright_id = Some(String::from_utf8(copy)?);
|
||||
},
|
||||
_ => {
|
||||
data.seek(SeekFrom::Current(i64::from(size)))?;
|
||||
},
|
||||
metadata.insert(
|
||||
String::from_utf8(fourcc_b.to_vec())?,
|
||||
String::from_utf8(value)?,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
data.seek(SeekFrom::Current(i64::from(size)))?;
|
||||
}
|
||||
|
||||
Ok((name_id, author_id, copyright_id, properties))
|
||||
Ok((metadata, properties))
|
||||
}
|
||||
|
||||
pub(crate) fn write_to(
|
||||
data: &mut File,
|
||||
metadata: (Option<&String>, Option<&String>, Option<&String>),
|
||||
) -> Result<()> {
|
||||
pub(crate) fn write_to(data: &mut File, metadata: &HashMap<String, String>) -> Result<()> {
|
||||
verify_aiff(data)?;
|
||||
|
||||
let mut text_chunks = Vec::new();
|
||||
|
||||
if let Some(name_id) = metadata.0 {
|
||||
let len = (name_id.len() as u32).to_be_bytes();
|
||||
for (k, v) in metadata {
|
||||
let len = (v.len() as u32).to_be_bytes();
|
||||
|
||||
text_chunks.extend(b"NAME".iter());
|
||||
text_chunks.extend(k.as_bytes().iter());
|
||||
text_chunks.extend(len.iter());
|
||||
text_chunks.extend(name_id.as_bytes().iter());
|
||||
text_chunks.extend(v.as_bytes().iter());
|
||||
}
|
||||
|
||||
if let Some(author_id) = metadata.1 {
|
||||
let len = (author_id.len() as u32).to_be_bytes();
|
||||
|
||||
text_chunks.extend(b"AUTH".iter());
|
||||
text_chunks.extend(len.iter());
|
||||
text_chunks.extend(author_id.as_bytes().iter());
|
||||
}
|
||||
|
||||
if let Some(copyright_id) = metadata.2 {
|
||||
let len = (copyright_id.len() as u32).to_be_bytes();
|
||||
|
||||
text_chunks.extend(b"(c) ".iter());
|
||||
text_chunks.extend(len.iter());
|
||||
text_chunks.extend(copyright_id.as_bytes().iter());
|
||||
}
|
||||
|
||||
let mut name: Option<(usize, usize)> = None;
|
||||
let mut auth: Option<(usize, usize)> = None;
|
||||
let mut copy: Option<(usize, usize)> = None;
|
||||
let mut chunks_remove = Vec::new();
|
||||
|
||||
while let (Ok(fourcc), Ok(size)) = (
|
||||
data.read_u32::<LittleEndian>(),
|
||||
data.read_u32::<BigEndian>(),
|
||||
) {
|
||||
let fourcc_b = &fourcc.to_le_bytes();
|
||||
let pos = (data.seek(SeekFrom::Current(0))? - 8) as usize;
|
||||
|
||||
match &fourcc.to_le_bytes() {
|
||||
f if f == b"NAME" && name.is_none() => name = Some((pos, (pos + 8 + size as usize))),
|
||||
f if f == b"AUTH" && auth.is_none() => auth = Some((pos, (pos + 8 + size as usize))),
|
||||
f if f == b"(c) " && copy.is_none() => copy = Some((pos, (pos + 8 + size as usize))),
|
||||
_ => {
|
||||
data.seek(SeekFrom::Current(i64::from(size)))?;
|
||||
continue;
|
||||
},
|
||||
if fourcc_b == b"NAME" || fourcc_b == b"AUTH" || fourcc_b == b"(c) " {
|
||||
chunks_remove.push((pos, (pos + 8 + size as usize)))
|
||||
}
|
||||
|
||||
data.seek(SeekFrom::Current(i64::from(size)))?;
|
||||
|
@ -241,43 +197,25 @@ cfg_if::cfg_if! {
|
|||
let mut file_bytes = Vec::new();
|
||||
data.read_to_end(&mut file_bytes)?;
|
||||
|
||||
match (name, auth, copy) {
|
||||
(None, None, None) => {
|
||||
data.seek(SeekFrom::Start(16))?;
|
||||
if chunks_remove.is_empty() {
|
||||
data.seek(SeekFrom::Start(16))?;
|
||||
|
||||
let mut size = [0; 4];
|
||||
data.read_exact(&mut size)?;
|
||||
let mut size = [0; 4];
|
||||
data.read_exact(&mut size)?;
|
||||
|
||||
let comm_end = (20 + u32::from_le_bytes(size)) as usize;
|
||||
file_bytes.splice(comm_end..comm_end, text_chunks);
|
||||
},
|
||||
(Some(single_value), None, None)
|
||||
| (None, Some(single_value), None)
|
||||
| (None, None, Some(single_value)) => {
|
||||
file_bytes.splice(single_value.0..single_value.1, text_chunks);
|
||||
},
|
||||
#[rustfmt::skip]
|
||||
(Some(a), Some(b), None)
|
||||
| (Some(a), None, Some(b))
|
||||
| (None, Some(a), Some(b)) => {
|
||||
let first = min(a, b);
|
||||
let end = max(a, b);
|
||||
let comm_end = (20 + u32::from_le_bytes(size)) as usize;
|
||||
file_bytes.splice(comm_end..comm_end, text_chunks);
|
||||
} else {
|
||||
chunks_remove.sort_unstable();
|
||||
chunks_remove.reverse();
|
||||
|
||||
file_bytes.drain(end.0..end.1);
|
||||
file_bytes.splice(first.0..first.1, text_chunks);
|
||||
},
|
||||
(Some(title), Some(author), Some(copyright)) => {
|
||||
let mut items = vec![title, author, copyright];
|
||||
items.sort_unstable();
|
||||
let first = chunks_remove.pop().unwrap();
|
||||
|
||||
let first = items[0];
|
||||
let mid = items[1];
|
||||
let end = items[2];
|
||||
for (s, e) in &chunks_remove {
|
||||
file_bytes.drain(*s as usize..*e as usize);
|
||||
}
|
||||
|
||||
file_bytes.drain(end.0..end.1);
|
||||
file_bytes.drain(mid.0..mid.1);
|
||||
file_bytes.splice(first.0..first.1, text_chunks);
|
||||
},
|
||||
file_bytes.splice(first.0 as usize..first.1 as usize, text_chunks);
|
||||
}
|
||||
|
||||
let total_size = ((file_bytes.len() - 8) as u32).to_be_bytes();
|
||||
|
|
|
@ -8,8 +8,8 @@ use std::fs::File;
|
|||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||
use std::time::Duration;
|
||||
|
||||
use unicase::UniCase;
|
||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use unicase::UniCase;
|
||||
|
||||
struct Block {
|
||||
byte: u8,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use super::find_last_page;
|
||||
use crate::{FileProperties, LoftyError, Result};
|
||||
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::time::Duration;
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
|
|
|
@ -2,8 +2,8 @@ use super::find_last_page;
|
|||
use crate::components::logic::ogg::constants::VORBIS_SETUP_HEAD;
|
||||
use crate::{FileProperties, LoftyError, Result};
|
||||
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||
use std::time::Duration;
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
|
|
|
@ -4,16 +4,15 @@ use crate::{
|
|||
ToAnyTag,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
use lofty_attr::LoftyTag;
|
||||
use lofty_attr::{get_set_methods, LoftyTag};
|
||||
|
||||
#[derive(Default)]
|
||||
struct AiffInnerTag {
|
||||
name_id: Option<String>,
|
||||
author_id: Option<String>,
|
||||
copyright_id: Option<String>,
|
||||
data: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(LoftyTag)]
|
||||
|
@ -32,50 +31,35 @@ impl AiffTag {
|
|||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
let (name_id, author_id, copyright_id, properties) = aiff::read_from(reader)?;
|
||||
let (data, properties) = aiff::read_from(reader)?;
|
||||
|
||||
Ok(Self {
|
||||
inner: AiffInnerTag {
|
||||
name_id,
|
||||
author_id,
|
||||
copyright_id,
|
||||
},
|
||||
inner: AiffInnerTag { data },
|
||||
properties,
|
||||
_format: TagType::AiffText,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_value(&self, key: &str) -> Option<&str> {
|
||||
self.inner.data.get_key_value(key).map(|(_, v)| v.as_str())
|
||||
}
|
||||
|
||||
fn set_value<V>(&mut self, key: &str, val: V)
|
||||
where
|
||||
V: Into<String>,
|
||||
{
|
||||
self.inner.data.insert(key.into(), val.into());
|
||||
}
|
||||
|
||||
fn remove_key(&mut self, key: &str) {
|
||||
self.inner.data.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioTagEdit for AiffTag {
|
||||
fn title(&self) -> Option<&str> {
|
||||
self.inner.name_id.as_deref()
|
||||
}
|
||||
fn set_title(&mut self, title: &str) {
|
||||
self.inner.name_id = Some(title.to_string())
|
||||
}
|
||||
fn remove_title(&mut self) {
|
||||
self.inner.name_id = None
|
||||
}
|
||||
|
||||
fn artist(&self) -> Option<&str> {
|
||||
self.inner.author_id.as_deref()
|
||||
}
|
||||
fn set_artist(&mut self, artist: &str) {
|
||||
self.inner.author_id = Some(artist.to_string())
|
||||
}
|
||||
fn remove_artist(&mut self) {
|
||||
self.inner.author_id = None
|
||||
}
|
||||
|
||||
fn copyright(&self) -> Option<&str> {
|
||||
self.inner.copyright_id.as_deref()
|
||||
}
|
||||
fn set_copyright(&mut self, copyright: &str) {
|
||||
self.inner.copyright_id = Some(copyright.to_string())
|
||||
}
|
||||
fn remove_copyright(&mut self) {
|
||||
self.inner.copyright_id = None
|
||||
}
|
||||
get_set_methods!(title, "NAME");
|
||||
get_set_methods!(artist, "AUTH");
|
||||
get_set_methods!(copyright, "(c) ");
|
||||
|
||||
fn tag_type(&self) -> TagType {
|
||||
TagType::AiffText
|
||||
|
@ -88,13 +72,6 @@ impl AudioTagEdit for AiffTag {
|
|||
|
||||
impl AudioTagWrite for AiffTag {
|
||||
fn write_to(&self, file: &mut File) -> Result<()> {
|
||||
aiff::write_to(
|
||||
file,
|
||||
(
|
||||
self.inner.name_id.as_ref(),
|
||||
self.inner.author_id.as_ref(),
|
||||
self.inner.copyright_id.as_ref(),
|
||||
),
|
||||
)
|
||||
aiff::write_to(file, &self.inner.data)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue