1
0
Fork 0
musicutil/src/utils/formats/id3.rs

150 lines
4.5 KiB
Rust

use std::path::PathBuf;
use id3::TagLike;
use crate::types::{AudioFileInfo, ReplayGainData, ReplayGainRawData, Tags};
use super::{AudioContainer, AudioContainerFormat, AudioFormatError, BoxedError};
pub struct ID3AudioFormat {
container_type: AudioContainer,
id3_tags: id3::Tag,
path: Box<PathBuf>,
}
impl AudioContainerFormat for ID3AudioFormat {
fn get_tags(&self, allow_missing: bool) -> Result<Tags, BoxedError> {
let title = self.id3_tags.title();
let artist = self.id3_tags.artist();
if !allow_missing {
if title.is_none() {
return Err(Box::new(AudioFormatError::MissingTitle));
}
if artist.is_none() {
return Err(Box::new(AudioFormatError::MissingArtist));
}
}
Ok(Tags {
title: String::from(title.unwrap()),
artist: String::from(artist.unwrap()),
})
}
fn get_replaygain_data(&self) -> Option<ReplayGainData> {
let frames = self.id3_tags.frames();
let mut contains_replaygain_tags = false;
let mut replaygain_data = ReplayGainData {
track_gain: "".to_string(),
track_peak: "".to_string(),
};
for frame in frames {
if frame.id() == "TXXX" {
if let Some(extended_text) = frame.content().extended_text() {
match extended_text.description.as_str() {
"REPLAYGAIN_TRACK_GAIN" => {
contains_replaygain_tags = true;
replaygain_data.track_gain = extended_text.value.clone()
}
"REPLAYGAIN_TRACK_PEAK" => {
contains_replaygain_tags = true;
replaygain_data.track_peak = extended_text.value.clone()
}
_ => {}
}
}
}
}
if !contains_replaygain_tags {
None
} else {
Some(replaygain_data)
}
}
fn supports_replaygain(&self) -> bool {
true
}
fn set_title(&mut self, title: String) -> Result<(), BoxedError> {
self.id3_tags.set_title(title);
Ok(())
}
fn set_artist(&mut self, artist: String) -> Result<(), BoxedError> {
self.id3_tags.set_artist(artist);
Ok(())
}
fn set_replaygain_data(&mut self, data: ReplayGainRawData) -> Result<(), BoxedError> {
let frames = self.id3_tags.remove("TXXX");
for frame in frames {
if let Some(extended_text) = frame.content().extended_text() {
if extended_text.description.starts_with("REPLAYGAIN") {
continue;
}
}
self.id3_tags.add_frame(frame);
}
self.id3_tags.add_frame(id3::Frame::with_content(
"TXXX",
id3::Content::ExtendedText(id3::frame::ExtendedText {
description: "REPLAYGAIN_TRACK_GAIN".to_string(),
value: format!("{:.2} dB", data.track_gain),
}),
));
self.id3_tags.add_frame(id3::Frame::with_content(
"TXXX",
id3::Content::ExtendedText(id3::frame::ExtendedText {
description: "REPLAYGAIN_TRACK_PEAK".to_string(),
value: format!("{:.6}", data.track_peak),
}),
));
Ok(())
}
fn save_changes(&mut self) -> Result<(), BoxedError> {
self.id3_tags
.write_to_path(self.path.as_path(), id3::Version::Id3v24)?;
Ok(())
}
fn get_audio_file_info(&self, allow_missing_tags: bool) -> Result<AudioFileInfo, BoxedError> {
return Ok(AudioFileInfo {
tags: self.get_tags(allow_missing_tags)?,
replaygain: self.get_replaygain_data(),
supports_replaygain: self.supports_replaygain(),
container: self.container_type,
});
}
}
pub fn new_id3_format_handler(
path: &PathBuf,
container_type: AudioContainer,
) -> Result<ID3AudioFormat, BoxedError> {
let id3_tags = match container_type {
// Only works on ID3 containing WAV files, but doesn't seem very widespread
AudioContainer::WAV => id3::Tag::read_from_wav_path(path)?,
AudioContainer::AIFF => id3::Tag::read_from_aiff_path(path)?,
_ => id3::Tag::read_from_path(path)?,
};
Ok(ID3AudioFormat {
container_type,
id3_tags,
path: Box::new(path.clone()),
})
}