Skip to content

Commit

Permalink
misc: Introduce WriteOptions
Browse files Browse the repository at this point in the history
This allows the caller to tweak how Lofty writes their tags in various ways.

As this is just a dumping ground for all sorts of format-specific settings, this is best used as an application global config that gets set once.

In its current state, it will only respect `uppercase_id3v2_chunk` and `preferred_padding` (for some formats).

`respect_read_only` and `remove_others` are defined for later use.

closes #228
  • Loading branch information
Serial-ATA committed Apr 3, 2024
1 parent a54861b commit d8080e4
Show file tree
Hide file tree
Showing 32 changed files with 1,058 additions and 477 deletions.
741 changes: 476 additions & 265 deletions CHANGELOG.md

Large diffs are not rendered by default.

46 changes: 23 additions & 23 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
[package]
name = "lofty"
version = "0.18.2"
authors = ["Serial <69764315+Serial-ATA@users.noreply.github.com>"]
edition = "2021"
license = "MIT OR Apache-2.0"
name = "lofty"
version = "0.18.2"
authors = ["Serial <69764315+Serial-ATA@users.noreply.github.com>"]
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Audio metadata library"
repository = "https://github.com/Serial-ATA/lofty-rs"
keywords = ["tags", "audio", "metadata", "id3", "vorbis"]
categories = ["multimedia", "multimedia::audio", "parser-implementations"]
readme = "README.md"
include = ["src", "LICENSE-APACHE", "LICENSE-MIT", "SUPPORTED_FORMATS.md"]
repository = "https://github.com/Serial-ATA/lofty-rs"
keywords = ["tags", "audio", "metadata", "id3", "vorbis"]
categories = ["multimedia", "multimedia::audio", "parser-implementations"]
readme = "README.md"
include = ["src", "LICENSE-APACHE", "LICENSE-MIT", "SUPPORTED_FORMATS.md"]

[dependencies]
# Vorbis comments pictures
data-encoding = "2.5.0"
byteorder = "1.5.0"
byteorder = "1.5.0"
# ID3 compressed frames
flate2 = { version = "1.0.28", optional = true }
flate2 = { version = "1.0.28", optional = true }
# Proc macros
lofty_attr = "0.9.0"
lofty_attr = { path = "lofty_attr" }
# Debug logging
log = "0.4.20"
log = "0.4.20"
# OGG Vorbis/Opus
ogg_pager = "0.6.0"
ogg_pager = "0.6.0"
# Key maps
paste = "1.0.14"
paste = "1.0.14"

[features]
default = ["id3v2_compression_support"]
default = ["id3v2_compression_support"]
id3v2_compression_support = ["dep:flate2"]

[dev-dependencies]
# WAV properties validity tests
hound = { git = "https://github.com/ruuda/hound.git", rev = "02e66effb33683dd6acb92df792683ee46ad6a59" }
hound = { git = "https://github.com/ruuda/hound.git", rev = "02e66effb33683dd6acb92df792683ee46ad6a59" }
# tag_writer example
structopt = { version = "0.3.26", default-features = false }
tempfile = "3.9.0"
tempfile = "3.9.0"

# Pretty heavy dependency, we don't want this compiling for test/doc runs
[target.'cfg(bench)'.dev-dependencies]
Expand All @@ -45,13 +45,13 @@ criterion = { version = "0.5.1", features = ["html_reports"] }
bench = false

[[bench]]
name = "read_file"
path = "benches/read_file.rs"
name = "read_file"
path = "benches/read_file.rs"
harness = false

[[bench]]
name = "create_tag"
path = "benches/create_tag.rs"
name = "create_tag"
path = "benches/create_tag.rs"
harness = false

[[example]]
Expand Down
4 changes: 2 additions & 2 deletions examples/tag_writer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use lofty::{Accessor, Probe, Tag, TagExt, TaggedFileExt};
use lofty::{Accessor, Probe, Tag, TagExt, TaggedFileExt, WriteOptions};

use structopt::StructOpt;

Expand Down Expand Up @@ -75,7 +75,7 @@ fn main() {
tag.set_genre(genre)
}

tag.save_to_path(&opt.path)
tag.save_to_path(&opt.path, WriteOptions::new())
.expect("ERROR: Failed to write the tag!");

println!("INFO: Tag successfully updated!");
Expand Down
14 changes: 7 additions & 7 deletions lofty_attr/src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,32 @@ pub(crate) fn init_write_lookup(
read_only: false,
items: lofty::ape::tag::tagitems_into_ape(tag),
}
.write_to(data)
.write_to(data, write_options)
});

insert!(map, Id3v1, {
Into::<lofty::id3::v1::tag::Id3v1TagRef<'_>>::into(tag).write_to(data)
Into::<lofty::id3::v1::tag::Id3v1TagRef<'_>>::into(tag).write_to(data, write_options)
});

if id3v2_strippable {
insert!(map, Id3v2, {
lofty::id3::v2::tag::Id3v2TagRef::empty().write_to(data)
lofty::id3::v2::tag::Id3v2TagRef::empty().write_to(data, write_options)
});
} else {
insert!(map, Id3v2, {
lofty::id3::v2::tag::Id3v2TagRef {
flags: lofty::id3::v2::Id3v2TagFlags::default(),
frames: lofty::id3::v2::tag::tag_frames(tag),
}
.write_to(data)
.write_to(data, write_options)
});
}

insert!(map, RiffInfo, {
lofty::iff::wav::tag::RIFFInfoListRef::new(lofty::iff::wav::tag::tagitems_into_riff(
tag.items(),
))
.write_to(data)
.write_to(data, write_options)
});

insert!(map, AiffText, {
Expand All @@ -84,7 +84,7 @@ pub(crate) fn init_write_lookup(
annotations: Some(tag.get_strings(&lofty::tag::item::ItemKey::Comment)),
comments: None,
}
.write_to(data)
.write_to(data, write_options)
});

map
Expand All @@ -111,7 +111,7 @@ pub(crate) fn write_module(
quote! {
pub(crate) mod write {
#[allow(unused_variables)]
pub(crate) fn write_to(data: &mut ::std::fs::File, tag: &::lofty::Tag) -> ::lofty::error::Result<()> {
pub(crate) fn write_to(data: &mut ::std::fs::File, tag: &::lofty::Tag, write_options: ::lofty::WriteOptions) -> ::lofty::error::Result<()> {
match tag.tag_type() {
#( #applicable_formats )*
_ => crate::macros::err!(UnsupportedTag),
Expand Down
8 changes: 4 additions & 4 deletions lofty_attr/src/lofty_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ fn generate_audiofile_impl(file: &LoftyFile) -> syn::Result<proc_macro2::TokenSt
#read_fn(reader, parse_options)
}

fn save_to(&self, file: &mut ::std::fs::File) -> ::lofty::error::Result<()> {
fn save_to(&self, file: &mut ::std::fs::File, write_options: ::lofty::WriteOptions) -> ::lofty::error::Result<()> {
use ::lofty::TagExt as _;
use ::std::io::Seek as _;
#save_to_body
Expand Down Expand Up @@ -480,7 +480,7 @@ fn get_save_to_body(
// Custom write fn
if let Some(write_fn) = write_fn {
return quote! {
#write_fn(&self, file)
#write_fn(&self, file, write_options)
};
}

Expand All @@ -490,13 +490,13 @@ fn get_save_to_body(
quote! {
if let Some(ref tag) = self.#name {
file.rewind()?;
tag.save_to(file)?;
tag.save_to(file, write_options)?;
}
}
} else {
quote! {
file.rewind()?;
self.#name.save_to(file)?;
self.#name.save_to(file, write_options)?;
}
}
});
Expand Down
33 changes: 24 additions & 9 deletions src/ape/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::id3::v2::util::pairs::{format_number_pair, set_number, NUMBER_PAIR_KE
use crate::tag::item::{ItemKey, ItemValue, ItemValueRef, TagItem};
use crate::tag::{try_parse_year, Tag, TagType};
use crate::traits::{Accessor, MergeTag, SplitTag, TagExt};
use crate::write_options::WriteOptions;

use std::borrow::Cow;
use std::fs::File;
Expand Down Expand Up @@ -320,25 +321,33 @@ impl TagExt for ApeTag {
///
/// * Attempting to write the tag to a format that does not support it
/// * An existing tag has an invalid size
fn save_to(&self, file: &mut File) -> std::result::Result<(), Self::Err> {
fn save_to(
&self,
file: &mut File,
write_options: WriteOptions,
) -> std::result::Result<(), Self::Err> {
ApeTagRef {
read_only: self.read_only,
items: self.items.iter().map(Into::into),
}
.write_to(file)
.write_to(file, write_options)
}

/// Dumps the tag to a writer
///
/// # Errors
///
/// * [`std::io::Error`]
fn dump_to<W: Write>(&self, writer: &mut W) -> std::result::Result<(), Self::Err> {
fn dump_to<W: Write>(
&self,
writer: &mut W,
write_options: WriteOptions,
) -> std::result::Result<(), Self::Err> {
ApeTagRef {
read_only: self.read_only,
items: self.items.iter().map(Into::into),
}
.dump_to(writer)
.dump_to(writer, write_options)
}

fn remove_from_path<P: AsRef<Path>>(&self, path: P) -> std::result::Result<(), Self::Err> {
Expand Down Expand Up @@ -482,11 +491,15 @@ impl<'a, I> ApeTagRef<'a, I>
where
I: Iterator<Item = ApeItemRef<'a>>,
{
pub(crate) fn write_to(&mut self, file: &mut File) -> Result<()> {
write::write_to(file, self)
pub(crate) fn write_to(&mut self, file: &mut File, write_options: WriteOptions) -> Result<()> {
write::write_to(file, self, write_options)
}

pub(crate) fn dump_to<W: Write>(&mut self, writer: &mut W) -> Result<()> {
pub(crate) fn dump_to<W: Write>(
&mut self,
writer: &mut W,
_write_options: WriteOptions,
) -> Result<()> {
let temp = write::create_ape_tag(self)?;
writer.write_all(&temp)?;

Expand Down Expand Up @@ -531,7 +544,7 @@ pub(crate) fn tagitems_into_ape(tag: &Tag) -> impl Iterator<Item = ApeItemRef<'_
#[cfg(test)]
mod tests {
use crate::ape::{ApeItem, ApeTag};
use crate::{Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType};
use crate::{Accessor, ItemKey, ItemValue, Tag, TagExt, TagItem, TagType, WriteOptions};

use crate::id3::v2::util::pairs::DEFAULT_NUMBER_IN_PAIR;
use std::io::Cursor;
Expand Down Expand Up @@ -608,7 +621,9 @@ mod tests {
.unwrap();

let mut writer = Vec::new();
parsed_tag.dump_to(&mut writer).unwrap();
parsed_tag
.dump_to(&mut writer, WriteOptions::new())
.unwrap();

let mut temp_reader = Cursor::new(writer);

Expand Down
8 changes: 7 additions & 1 deletion src/ape/tag/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ use crate::id3::{find_id3v1, find_id3v2, find_lyrics3v2, FindId3v2Config};
use crate::macros::{decode_err, err};
use crate::probe::Probe;
use crate::tag::item::ItemValueRef;
use crate::write_options::WriteOptions;

use std::fs::File;
use std::io::{Cursor, Read, Seek, SeekFrom, Write};

use byteorder::{LittleEndian, WriteBytesExt};

#[allow(clippy::shadow_unrelated)]
pub(crate) fn write_to<'a, I>(data: &mut File, tag: &mut ApeTagRef<'a, I>) -> Result<()>
pub(crate) fn write_to<'a, I>(
data: &mut File,
tag: &mut ApeTagRef<'a, I>,
_write_options: WriteOptions,
) -> Result<()>
where
I: Iterator<Item = ApeItemRef<'a>>,
{
Expand All @@ -40,6 +45,7 @@ where
// If one is found, it'll be removed and rewritten at the bottom, where it should be
let mut header_ape_tag = (false, (0, 0));

// TODO: Respect read only
let start = data.stream_position()?;
match read::read_ape_tag(data, false)? {
Some((mut existing_tag, header)) => {
Expand Down
Loading

0 comments on commit d8080e4

Please sign in to comment.