diff --git a/lofty/src/mp4/ilst/write.rs b/lofty/src/mp4/ilst/write.rs index faba3dcea..cb639e68b 100644 --- a/lofty/src/mp4/ilst/write.rs +++ b/lofty/src/mp4/ilst/write.rs @@ -230,17 +230,7 @@ fn save_to_existing( ParseOptions::DEFAULT_PARSING_MODE, )?; - if tree.is_empty() { - // Nothing to do - if remove_tag { - return Ok(()); - } - - let meta_end = (meta.start + meta.len) as usize; - - replacement = ilst; - range = meta_end..meta_end; - } else { + if let Some(ilst_idx) = ilst_idx { let existing_ilst = &tree[ilst_idx]; let existing_ilst_size = existing_ilst.len; @@ -315,6 +305,16 @@ fn save_to_existing( replacement = ilst; range = range_start as usize..range_end as usize; } + } else { + // Nothing to do + if remove_tag { + return Ok(()); + } + + let meta_end = (meta.start + meta.len) as usize; + + replacement = ilst; + range = meta_end..meta_end; } drop(write_handle); diff --git a/lofty/src/mp4/read/mod.rs b/lofty/src/mp4/read/mod.rs index f55bbb60a..3ff9fec56 100644 --- a/lofty/src/mp4/read/mod.rs +++ b/lofty/src/mp4/read/mod.rs @@ -156,11 +156,11 @@ pub(super) fn atom_tree( mut len: u64, up_to: &[u8], parse_mode: ParsingMode, -) -> Result<(usize, Vec)> +) -> Result<(Option, Vec)> where R: Read + Seek, { - let mut found_idx: usize = 0; + let mut found_idx: Option = None; let mut buf = Vec::new(); let mut i = 0; @@ -174,18 +174,15 @@ where len = len.saturating_sub(atom.len); if let AtomIdent::Fourcc(ref fourcc) = atom.ident { - i += 1; - if fourcc == up_to { - found_idx = i; + found_idx = Some(i); } + i += 1; buf.push(atom); } } - found_idx = found_idx.saturating_sub(1); - Ok((found_idx, buf)) } diff --git a/lofty/tests/files/mp4.rs b/lofty/tests/files/mp4.rs index 08f7a8ad0..7d41602b6 100644 --- a/lofty/tests/files/mp4.rs +++ b/lofty/tests/files/mp4.rs @@ -73,3 +73,33 @@ fn read_no_properties() { fn read_no_tags() { crate::util::no_tag_test("tests/files/assets/minimal/m4a_codec_aac.m4a", None); } + +#[test_log::test] +fn hdlr_preservation_on_tag_recreation() { + use lofty::tag::Tag; + use std::fs; + + let src = "tests/files/assets/minimal/m4a_codec_aac.m4a"; + let temp_path = "tests/files/assets/minimal/temp_hdlr_test.m4a"; + let _ = fs::remove_file(temp_path); + fs::copy(src, temp_path).unwrap(); + + // 1. Remove all tags + TagType::Mp4Ilst.remove_from_path(temp_path).unwrap(); + + // 2. Open the file and write a brand new tag + let mut tagged_file = Probe::open(temp_path).unwrap().read().unwrap(); + let tag = Tag::new(TagType::Mp4Ilst); + tagged_file.insert_tag(tag); + tagged_file + .save_to_path(temp_path, lofty::config::WriteOptions::default()) + .unwrap(); + + // 3. Read the file bytes and verify that "hdlr" is present + let bytes = fs::read(temp_path).unwrap(); + let has_hdlr = bytes.windows(4).any(|w| w == b"hdlr"); + + let _ = fs::remove_file(temp_path); + + assert!(has_hdlr, "Output M4A is missing the hdlr atom!"); +}