diff --git a/src/model/entry.rs b/src/model/entry.rs index 981333b..50b8e3a 100644 --- a/src/model/entry.rs +++ b/src/model/entry.rs @@ -1,6 +1,7 @@ use crate::*; use std::fs::File; use std::borrow::Cow; +use std::collections::HashSet; use std::io::{BufReader, Read}; use std::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering}; use byte_unit::{Byte, Unit::MB}; @@ -75,7 +76,17 @@ impl Entry { } pub fn is_modified (&self) -> bool { match &*self.info.read().unwrap() { - Metadata::Music { modified_tag, .. } => modified_tag.is_some(), + Metadata::Music { original_tag, modified_tag, .. } => match ( + original_tag, + modified_tag, + ) { + (Some(original), Some(modified)) => { + let original_items: HashSet<_> = original.items().collect(); + original_items != modified.read().unwrap().items().collect() + }, + (None, None) => false, + _ => true, + }, _ => false, } } @@ -91,24 +102,26 @@ impl PartialOrd for Entry { pub enum Metadata { Directory { - hash_file: Option<()>, + hash_file: Option<()>, catalog_file: Option<()>, - artist_file: Option<()>, + artist_file: Option<()>, release_file: Option<()>, }, Music { - invalid: bool, - hash: Arc, - size: Arc, + invalid: bool, + hash: Arc, + size: Arc, + /// The tag that was read from the file original_tag: Option>, + /// The tag that will be written to the file modified_tag: Option>>, }, Image { invalid: bool, - hash: Arc, - size: Arc, - title: Option, - author: Option, + hash: Arc, + size: Arc, + title: Option, + author: Option, }, Unknown { hash: Arc, @@ -136,7 +149,7 @@ impl Metadata { size: format!("{:#>8.2}", size).into(), invalid: false, original_tag: tag.map(|t|t.clone().into()), - modified_tag: None, + modified_tag: tag.map(|t|Arc::new(t.clone().into())), }) } else { Self::new_fallback(path) @@ -226,49 +239,88 @@ macro_rules! music_tag_field { } /// Set or delete the metadata value. /// Return whether the changes should be recounted. - pub fn $set (&mut self, raw_value: Option<&str>) -> bool { + pub fn $set (&mut self, value: Option<&str>) -> bool { let parse = $parse; - if let &mut Metadata::Music { ref mut modified_tag, .. } = self { - //let write = $write; - if let Some(raw_value) = raw_value { - // Set value - if let Ok(parsed_value) = parse(raw_value) { - match &modified_tag { - Some(new_tag) => { - // add entry to modified_tag - new_tag.write().unwrap().$set(parsed_value); - true - }, - None => { - // first entry creates modified_tag - let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types - new_tag.$set(parsed_value); - *modified_tag = Some(Arc::new(RwLock::new(new_tag))); - true - }, - } + match self { + + &mut Metadata::Music { + original_tag: Some(ref original), modified_tag: Some(ref modified), .. + } => if let Some(value) = value { + // set new value + let value = parse(value).unwrap(); + let changed = original.$get() != Some(value); + modified.write().unwrap().$set(value); + changed + } else { + let modified = modified.write().unwrap(); + if let Some(value) = modified.$get() { + // delete modified value + modified.$del(); + true + } else if let Some(value) = original.$get() { + // set to original value + modified.$set(value); + true + } else { + false + } + }, + + &mut Metadata::Music { + original_tag: Some(ref original), ref mut modified_tag, .. + } => if let Some(input_value) = value { + let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types + new_tag.$set(input_value); + *modified_tag = Some(Arc::new(RwLock::new(new_tag))); + true + } else if let Some(original_value) = original.$get() { + // set to original value + let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types + new_tag.$set(original_value); + *modified_tag = Some(Arc::new(RwLock::new(new_tag))); + true + } else { + false + }, + + &mut Metadata::Music { + original_tag: None, modified_tag: Some(ref modified), .. + } => if let Some(value) = value { + // set new value + let value = parse(value).unwrap(); + let modified = modified.write().unwrap(); + if modified.$get() != Some(value) { + // set modified value + modified.$set(value); + true } else { false } } else { - // Delete value - match &modified_tag { - Some(new_tag) => { - // remove entry from modified_tag - new_tag.write().unwrap().$del(); - if new_tag.read().unwrap().item_count() <= 1 { - // removing last entry removes modified_tag - *modified_tag = None; - } - true - }, - None => { - false - } + let modified = modified.write().unwrap(); + if modified.$get().is_some() { + // remove modified value + modified.$del(); + true + } else { + false } + }, + + &mut Metadata::Music { + original_tag: None, ref mut modified_tag, .. + } => if let Some(value) = value { + let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types + new_tag.$set(value); + *modified_tag = Some(Arc::new(RwLock::new(new_tag))); + true + } else { + false + }, + + _ => { + false } - } else { - false } } } @@ -288,7 +340,7 @@ generated_field!(size = |self|match self { }); music_tag_field!(artist set_artist remove_artist ItemKey::TrackArtist; write = |value: Cow|value.into(); - parse = |value: &str|Ok::(value.trim().into());); + parse = |value: &str|Ok::(value.trim());); music_tag_field!(year set_year remove_year ItemKey::Year; write = |value: u32|format!("{value}").into(); parse = |value: &str|value.trim().parse::(););