Start work on duration

Added a duration field to each tag struct, and implemented duration reading for mp3. All other tags return None for now.
This commit is contained in:
Serial 2021-04-20 15:15:05 -04:00
parent 0dd57cc4da
commit 8ff6bb52c8
9 changed files with 200 additions and 149 deletions

View file

@ -1,13 +1,16 @@
#![cfg(feature = "ape")]
use crate::{
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Picture,
Result, TagType, ToAny, ToAnyTag,
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Picture, Result, TagType,
ToAny, ToAnyTag,
};
pub use ape::Tag as ApeInnerTag;
use filepath::FilePath;
use std::{fs::File, path::Path};
use std::fs::File;
use std::path::Path;
#[cfg(feature = "duration")]
use std::time::Duration;
impl_tag!(ApeTag, ApeInnerTag, TagType::Ape);
@ -17,7 +20,11 @@ impl ApeTag {
where
P: AsRef<Path>,
{
Ok(Self(ape::read(path)?))
Ok(Self {
inner: ape::read(&path)?,
#[cfg(feature = "duration")]
duration: None, // TODO
})
}
}
@ -38,7 +45,8 @@ impl<'a> From<&'a ApeTag> for AnyTag<'a> {
total_discs: inp.total_discs(),
comments: None,
date: None, // TODO
duration_ms: None,
#[cfg(feature = "duration")]
duration: None,
}
}
}
@ -81,7 +89,7 @@ impl<'a> From<AnyTag<'a>> for ApeTag {
impl ApeTag {
fn get_value(&self, key: &str) -> Option<&str> {
if let Some(item) = self.0.item(key) {
if let Some(item) = self.inner.item(key) {
if let ape::ItemValue::Text(val) = &item.value {
return Some(&*val);
}
@ -99,11 +107,11 @@ impl ApeTag {
value: ape::ItemValue::Text(val.into()),
};
self.0.set_item(item)
self.inner.set_item(item)
}
fn remove_key(&mut self, key: &str) {
let _ = self.0.remove_item(key);
let _ = self.inner.remove_item(key);
}
}
@ -284,11 +292,11 @@ impl AudioTagEdit for ApeTag {
impl AudioTagWrite for ApeTag {
fn write_to(&self, file: &mut File) -> Result<()> {
// Write only uses paths, this is annoying
ape::write(&self.0, file.path()?)?;
ape::write(&self.inner, file.path()?)?;
Ok(())
}
fn write_to_path(&self, path: &str) -> Result<()> {
ape::write(&self.0, path)?;
ape::write(&self.inner, path)?;
Ok(())
}
}

View file

@ -1,14 +1,16 @@
#![cfg(feature = "mp3")]
use crate::{
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error,
MimeType, Picture, Result, TagType, ToAny, ToAnyTag,
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error, MimeType, Picture,
Result, TagType, ToAny, ToAnyTag,
};
pub use id3::Tag as Id3v2InnerTag;
use std::convert::TryInto;
use std::fs::File;
use std::path::Path;
#[cfg(feature = "duration")]
use std::time::Duration;
impl_tag!(Id3v2Tag, Id3v2InnerTag, TagType::Id3v2);
@ -18,7 +20,11 @@ impl Id3v2Tag {
where
P: AsRef<Path>,
{
Ok(Self(Id3v2InnerTag::read_from_path(path)?))
Ok(Self {
inner: Id3v2InnerTag::read_from_path(&path)?,
#[cfg(feature = "duration")]
duration: Some(mp3_duration::from_path(&path)?), // TODO
})
}
}
@ -39,7 +45,8 @@ impl<'a> From<&'a Id3v2Tag> for AnyTag<'a> {
total_discs: inp.total_discs(),
comments: None,
date: None, // TODO
duration_ms: None,
#[cfg(feature = "duration")]
duration: inp.duration,
}
}
}
@ -98,21 +105,21 @@ impl<'a> std::convert::TryFrom<&'a id3::frame::Picture> for Picture<'a> {
impl AudioTagEdit for Id3v2Tag {
fn title(&self) -> Option<&str> {
self.0.title()
self.inner.title()
}
fn set_title(&mut self, title: &str) {
self.0.set_title(title)
self.inner.set_title(title)
}
fn remove_title(&mut self) {
self.0.remove_title();
self.inner.remove_title();
}
fn artist_str(&self) -> Option<&str> {
self.0.artist()
self.inner.artist()
}
fn set_artist(&mut self, artist: &str) {
self.0.set_artist(artist)
self.inner.set_artist(artist)
}
fn artists_vec(&self) -> Option<Vec<&str>> {
@ -120,47 +127,47 @@ impl AudioTagEdit for Id3v2Tag {
}
fn remove_artist(&mut self) {
self.0.remove_artist()
self.inner.remove_artist()
}
fn year(&self) -> Option<i32> {
self.0.year()
self.inner.year()
}
fn set_year(&mut self, year: i32) {
self.0.set_year(year as i32)
self.inner.set_year(year as i32)
}
fn remove_year(&mut self) {
self.0.remove_year()
self.inner.remove_year()
}
fn album_title(&self) -> Option<&str> {
self.0.album()
self.inner.album()
}
fn set_album_title(&mut self, v: &str) {
self.0.set_album(v)
self.inner.set_album(v)
}
fn remove_album_title(&mut self) {
self.0.remove_album();
self.inner.remove_album();
}
fn album_artist_str(&self) -> Option<&str> {
self.0.album_artist()
self.inner.album_artist()
}
fn album_artists_vec(&self) -> Option<Vec<&str>> {
self.0.album_artist().map(|a| a.split('/').collect())
self.inner.album_artist().map(|a| a.split('/').collect())
}
fn set_album_artist(&mut self, artists: &str) {
self.0.set_album_artist(artists)
self.inner.set_album_artist(artists)
}
fn remove_album_artists(&mut self) {
self.0.remove_album_artist()
self.inner.remove_album_artist()
}
fn album_cover(&self) -> Option<Picture> {
self.0
self.inner
.pictures()
.find(|&pic| matches!(pic.picture_type, id3::frame::PictureType::CoverFront))
.and_then(|pic| {
@ -172,7 +179,7 @@ impl AudioTagEdit for Id3v2Tag {
}
fn set_album_cover(&mut self, cover: Picture) {
self.remove_album_cover();
self.0.add_picture(id3::frame::Picture {
self.inner.add_picture(id3::frame::Picture {
mime_type: String::from(cover.mime_type),
picture_type: id3::frame::PictureType::CoverFront,
description: "".to_owned(),
@ -180,58 +187,58 @@ impl AudioTagEdit for Id3v2Tag {
});
}
fn remove_album_cover(&mut self) {
self.0
self.inner
.remove_picture_by_type(id3::frame::PictureType::CoverFront);
}
fn track_number(&self) -> Option<u32> {
self.0.track()
self.inner.track()
}
fn set_track_number(&mut self, track: u32) {
self.0.set_track(track);
self.inner.set_track(track);
}
fn remove_track_number(&mut self) {
self.0.remove_track();
self.inner.remove_track();
}
fn total_tracks(&self) -> Option<u32> {
self.0.total_tracks()
self.inner.total_tracks()
}
fn set_total_tracks(&mut self, total_track: u32) {
self.0.set_total_tracks(total_track as u32);
self.inner.set_total_tracks(total_track as u32);
}
fn remove_total_tracks(&mut self) {
self.0.remove_total_tracks();
self.inner.remove_total_tracks();
}
fn disc_number(&self) -> Option<u32> {
self.0.disc()
self.inner.disc()
}
fn set_disc_number(&mut self, disc_number: u32) {
self.0.set_disc(disc_number as u32)
self.inner.set_disc(disc_number as u32)
}
fn remove_disc_number(&mut self) {
self.0.remove_disc();
self.inner.remove_disc();
}
fn total_discs(&self) -> Option<u32> {
self.0.total_discs()
self.inner.total_discs()
}
fn set_total_discs(&mut self, total_discs: u32) {
self.0.set_total_discs(total_discs)
self.inner.set_total_discs(total_discs)
}
fn remove_total_discs(&mut self) {
self.0.remove_total_discs();
self.inner.remove_total_discs();
}
}
impl AudioTagWrite for Id3v2Tag {
fn write_to(&self, file: &mut File) -> Result<()> {
self.0.write_to(file, id3::Version::Id3v24)?;
self.inner.write_to(file, id3::Version::Id3v24)?;
Ok(())
}
fn write_to_path(&self, path: &str) -> Result<()> {
self.0.write_to_path(path, id3::Version::Id3v24)?;
self.inner.write_to_path(path, id3::Version::Id3v24)?;
Ok(())
}
}

View file

@ -1,13 +1,15 @@
#![cfg(feature = "mp4")]
use crate::{
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error,
MimeType, Picture, Result, TagType, ToAny, ToAnyTag,
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error, MimeType, Picture,
Result, TagType, ToAny, ToAnyTag,
};
pub use mp4ameta::{FourCC, Tag as Mp4InnerTag};
use std::fs::File;
use std::path::Path;
#[cfg(feature = "duration")]
use std::time::Duration;
impl_tag!(Mp4Tag, Mp4InnerTag, TagType::Mp4);
@ -17,7 +19,11 @@ impl Mp4Tag {
where
P: AsRef<Path>,
{
Ok(Self(Mp4InnerTag::read_from_path(path)?))
Ok(Self {
inner: Mp4InnerTag::read_from_path(path)?,
#[cfg(feature = "duration")]
duration: None,
})
}
}
@ -45,7 +51,8 @@ impl<'a> From<&'a Mp4Tag> for AnyTag<'a> {
total_discs,
comments: None,
date: None,
duration_ms: None, // TODO?
#[cfg(feature = "duration")]
duration: None, // TODO?
}
}
}
@ -104,62 +111,62 @@ impl<'a> std::convert::TryFrom<&'a mp4ameta::Data> for Picture<'a> {
impl AudioTagEdit for Mp4Tag {
fn title(&self) -> Option<&str> {
self.0.title()
self.inner.title()
}
fn set_title(&mut self, title: &str) {
self.0.set_title(title)
self.inner.set_title(title)
}
fn remove_title(&mut self) {
self.0.remove_title();
self.inner.remove_title();
}
fn artist_str(&self) -> Option<&str> {
self.0.artist()
self.inner.artist()
}
fn set_artist(&mut self, artist: &str) {
self.0.set_artist(artist)
self.inner.set_artist(artist)
}
fn remove_artist(&mut self) {
self.0.remove_artists();
self.inner.remove_artists();
}
fn year(&self) -> Option<i32> {
self.0.year().and_then(|x| str::parse(x).ok())
self.inner.year().and_then(|x| str::parse(x).ok())
}
fn set_year(&mut self, year: i32) {
self.0.set_year(year.to_string())
self.inner.set_year(year.to_string())
}
fn remove_year(&mut self) {
self.0.remove_year();
self.inner.remove_year();
}
fn album_title(&self) -> Option<&str> {
self.0.album()
self.inner.album()
}
fn set_album_title(&mut self, v: &str) {
self.0.set_album(v)
self.inner.set_album(v)
}
fn remove_album_title(&mut self) {
self.0.remove_album();
self.inner.remove_album();
}
fn album_artist_str(&self) -> Option<&str> {
self.0.album_artist()
self.inner.album_artist()
}
fn set_album_artist(&mut self, artists: &str) {
self.0.set_album_artist(artists)
self.inner.set_album_artist(artists)
}
fn remove_album_artists(&mut self) {
self.0.remove_album_artists();
self.inner.remove_album_artists();
}
fn album_cover(&self) -> Option<Picture> {
use mp4ameta::Data::{Jpeg, Png};
self.0.artwork().and_then(|data| match data {
self.inner.artwork().and_then(|data| match data {
Jpeg(d) => Some(Picture {
data: d,
mime_type: MimeType::Jpeg,
@ -174,67 +181,67 @@ impl AudioTagEdit for Mp4Tag {
fn set_album_cover(&mut self, cover: Picture) {
self.remove_album_cover();
self.0.add_artwork(match cover.mime_type {
self.inner.add_artwork(match cover.mime_type {
MimeType::Png => mp4ameta::Data::Png(cover.data.to_owned()),
MimeType::Jpeg => mp4ameta::Data::Jpeg(cover.data.to_owned()),
_ => panic!("Only png and jpeg are supported in m4a"),
});
}
fn remove_album_cover(&mut self) {
self.0.remove_artwork();
self.inner.remove_artwork();
}
fn remove_track(&mut self) {
self.0.remove_track(); // faster than removing separately
self.inner.remove_track(); // faster than removing separately
}
fn track_number(&self) -> Option<u32> {
self.0.track_number().map(u32::from)
self.inner.track_number().map(u32::from)
}
fn set_track_number(&mut self, track: u32) {
self.0.set_track_number(track as u16);
self.inner.set_track_number(track as u16);
}
fn remove_track_number(&mut self) {
self.0.remove_track_number();
self.inner.remove_track_number();
}
fn total_tracks(&self) -> Option<u32> {
self.0.total_tracks().map(u32::from)
self.inner.total_tracks().map(u32::from)
}
fn set_total_tracks(&mut self, total_track: u32) {
self.0.set_total_tracks(total_track as u16);
self.inner.set_total_tracks(total_track as u16);
}
fn remove_total_tracks(&mut self) {
self.0.remove_total_tracks();
self.inner.remove_total_tracks();
}
fn remove_disc(&mut self) {
self.0.remove_disc();
self.inner.remove_disc();
}
fn disc_number(&self) -> Option<u32> {
self.0.disc_number().map(u32::from)
self.inner.disc_number().map(u32::from)
}
fn set_disc_number(&mut self, disc_number: u32) {
self.0.set_disc_number(disc_number as u16)
self.inner.set_disc_number(disc_number as u16)
}
fn remove_disc_number(&mut self) {
self.0.remove_disc_number();
self.inner.remove_disc_number();
}
fn total_discs(&self) -> Option<u32> {
self.0.total_discs().map(u32::from)
self.inner.total_discs().map(u32::from)
}
fn set_total_discs(&mut self, total_discs: u32) {
self.0.set_total_discs(total_discs as u16)
self.inner.set_total_discs(total_discs as u16)
}
fn remove_total_discs(&mut self) {
self.0.remove_total_discs();
self.inner.remove_total_discs();
}
}
impl AudioTagWrite for Mp4Tag {
fn write_to(&self, file: &mut File) -> Result<()> {
self.0.write_to(&file)?;
self.inner.write_to(&file)?;
Ok(())
}
fn write_to_path(&self, path: &str) -> Result<()> {
self.0.write_to_path(path)?;
self.inner.write_to_path(path)?;
Ok(())
}
}

View file

@ -10,6 +10,8 @@ use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::{Cursor, Seek, SeekFrom, Write};
use std::path::Path;
#[cfg(feature = "duration")]
use std::time::Duration;
const START_SIGNATURE: [u8; 7] = [3, 118, 111, 114, 98, 105, 115];
const END_BYTE: u8 = 1;
@ -118,7 +120,11 @@ impl VorbisTag {
where
P: AsRef<Path>,
{
Ok(Self(VorbisInnerTag::from_path(path, format)?))
Ok(Self {
inner: VorbisInnerTag::from_path(path, format)?,
#[cfg(feature = "duration")]
duration: None,
})
}
}
@ -204,7 +210,7 @@ impl From<metaflac::Tag> for VorbisTag {
(comments, vendor)
};
tag.0 = VorbisInnerTag {
tag.inner = VorbisInnerTag {
format: Some(VorbisFormat::Flac),
vendor,
comments,
@ -220,10 +226,10 @@ impl From<&VorbisTag> for metaflac::Tag {
tag.remove_blocks(metaflac::BlockType::VorbisComment);
let vendor = inp.0.vendor.clone();
let vendor = inp.inner.vendor.clone();
let mut comment_collection: HashMap<String, Vec<String>> = HashMap::new();
for (k, v) in inp.0.comments.clone() {
for (k, v) in inp.inner.comments.clone() {
comment_collection.insert(k, vec![v]);
}
@ -240,80 +246,80 @@ impl From<&VorbisTag> for metaflac::Tag {
impl AudioTagEdit for VorbisTag {
fn title(&self) -> Option<&str> {
self.0.get_value("TITLE")
self.inner.get_value("TITLE")
}
fn set_title(&mut self, title: &str) {
self.0.set_value("TITLE", title);
self.inner.set_value("TITLE", title);
}
fn remove_title(&mut self) {
self.0.remove_key("TITLE");
self.inner.remove_key("TITLE");
}
fn artist_str(&self) -> Option<&str> {
self.0.get_value("ARTIST")
self.inner.get_value("ARTIST")
}
fn set_artist(&mut self, artist: &str) {
self.0.set_value("ARTIST", artist)
self.inner.set_value("ARTIST", artist)
}
fn remove_artist(&mut self) {
self.0.remove_key("ARTIST");
self.inner.remove_key("ARTIST");
}
fn year(&self) -> Option<i32> {
if let Some(Ok(y)) = self
.0
.inner
.get_value("DATE")
.map(|s| s.chars().take(4).collect::<String>().parse::<i32>())
{
Some(y)
} else if let Some(Ok(y)) = self.0.get_value("YEAR").map(str::parse::<i32>) {
} else if let Some(Ok(y)) = self.inner.get_value("YEAR").map(str::parse::<i32>) {
Some(y)
} else {
None
}
}
fn set_year(&mut self, year: i32) {
self.0.set_value("DATE", &year.to_string());
self.0.set_value("YEAR", &year.to_string());
self.inner.set_value("DATE", &year.to_string());
self.inner.set_value("YEAR", &year.to_string());
}
fn remove_year(&mut self) {
self.0.remove_key("YEAR");
self.0.remove_key("DATE");
self.inner.remove_key("YEAR");
self.inner.remove_key("DATE");
}
fn album_title(&self) -> Option<&str> {
self.0.get_value("ALBUM")
self.inner.get_value("ALBUM")
}
fn set_album_title(&mut self, title: &str) {
self.0.set_value("ALBUM", title)
self.inner.set_value("ALBUM", title)
}
fn remove_album_title(&mut self) {
self.0.remove_key("ALBUM");
self.inner.remove_key("ALBUM");
}
fn album_artist_str(&self) -> Option<&str> {
self.0.get_value("ALBUMARTIST")
self.inner.get_value("ALBUMARTIST")
}
fn album_artists_vec(&self) -> Option<Vec<&str>> {
self.0
self.inner
.get_value("ALBUMARTIST")
.map(|a| a.split('/').collect())
}
fn set_album_artist(&mut self, artist: &str) {
self.0.set_value("ALBUMARTIST", artist)
self.inner.set_value("ALBUMARTIST", artist)
}
fn remove_album_artists(&mut self) {
self.0.remove_key("ALBUMARTIST");
self.inner.remove_key("ALBUMARTIST");
}
fn album_cover(&self) -> Option<Picture> {
// TODO
// self.0
// self.inner
// .pictures()
// .filter(|&pic| matches!(pic.picture_type, metaflac::block::PictureType::CoverFront))
// .next()
@ -330,84 +336,84 @@ impl AudioTagEdit for VorbisTag {
// self.remove_album_cover();
// let mime = String::from(cover.mime_type);
// let picture_type = metaflac::block::PictureType::CoverFront;
// self.0
// self.inner
// .add_picture(mime, picture_type, (cover.data).to_owned());
}
fn remove_album_cover(&mut self) {
// TODO
// self.0
// self.inner
// .remove_picture_type(metaflac::block::PictureType::CoverFront)
}
fn track_number(&self) -> Option<u32> {
if let Some(Ok(n)) = self.0.get_value("TRACKNUMBER").map(str::parse::<u32>) {
if let Some(Ok(n)) = self.inner.get_value("TRACKNUMBER").map(str::parse::<u32>) {
Some(n)
} else {
None
}
}
fn set_track_number(&mut self, v: u32) {
self.0.set_value("TRACKNUMBER", &v.to_string())
self.inner.set_value("TRACKNUMBER", &v.to_string())
}
fn remove_track_number(&mut self) {
self.0.remove_key("TRACKNUMBER");
self.inner.remove_key("TRACKNUMBER");
}
// ! not standard
fn total_tracks(&self) -> Option<u32> {
if let Some(Ok(n)) = self.0.get_value("TOTALTRACKS").map(str::parse::<u32>) {
if let Some(Ok(n)) = self.inner.get_value("TOTALTRACKS").map(str::parse::<u32>) {
Some(n)
} else {
None
}
}
fn set_total_tracks(&mut self, v: u32) {
self.0.set_value("TOTALTRACKS", &v.to_string())
self.inner.set_value("TOTALTRACKS", &v.to_string())
}
fn remove_total_tracks(&mut self) {
self.0.remove_key("TOTALTRACKS");
self.inner.remove_key("TOTALTRACKS");
}
fn disc_number(&self) -> Option<u32> {
if let Some(Ok(n)) = self.0.get_value("DISCNUMBER").map(str::parse::<u32>) {
if let Some(Ok(n)) = self.inner.get_value("DISCNUMBER").map(str::parse::<u32>) {
Some(n)
} else {
None
}
}
fn set_disc_number(&mut self, v: u32) {
self.0.set_value("DISCNUMBER", &v.to_string())
self.inner.set_value("DISCNUMBER", &v.to_string())
}
fn remove_disc_number(&mut self) {
self.0.remove_key("DISCNUMBER");
self.inner.remove_key("DISCNUMBER");
}
// ! not standard
fn total_discs(&self) -> Option<u32> {
if let Some(Ok(n)) = self.0.get_value("TOTALDISCS").map(str::parse::<u32>) {
if let Some(Ok(n)) = self.inner.get_value("TOTALDISCS").map(str::parse::<u32>) {
Some(n)
} else {
None
}
}
fn set_total_discs(&mut self, v: u32) {
self.0.set_value("TOTALDISCS", &v.to_string())
self.inner.set_value("TOTALDISCS", &v.to_string())
}
fn remove_total_discs(&mut self) {
self.0.remove_key("TOTALDISCS");
self.inner.remove_key("TOTALDISCS");
}
}
impl AudioTagWrite for VorbisTag {
fn write_to(&self, file: &mut File) -> Result<()> {
if let Some(format) = self.0.format.clone() {
if let Some(format) = self.inner.format.clone() {
match format {
VorbisFormat::Ogg => {
let vendor = self.0.vendor.clone();
let vendor = self.inner.vendor.clone();
let vendor_bytes = vendor.as_bytes();
let comments: Vec<(String, String)> = self
.0
.inner
.comments
.iter()
.map(|(a, b)| (a.to_string(), b.to_string()))

View file

@ -1,12 +1,15 @@
use crate::{
components::logic, impl_tag, Album, AnyTag, AudioTag, AudioTagEdit,
AudioTagWrite, Picture, Result, TagType, ToAny, ToAnyTag,
components::logic, impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Picture,
Result, TagType, ToAny, ToAnyTag,
};
use std::borrow::BorrowMut;
use std::fs::OpenOptions;
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::{Cursor, Seek, SeekFrom, Write};
use std::{collections::HashMap, fs::File, path::Path};
use std::path::Path;
#[cfg(feature = "duration")]
use std::time::Duration;
struct WavInnerTag {
data: Option<HashMap<String, String>>,
@ -23,12 +26,16 @@ impl Default for WavInnerTag {
impl WavTag {
#[allow(clippy::missing_errors_doc)]
pub fn read_from_path<P>(path: P) -> Result<Self>
where
P: AsRef<Path>,
where
P: AsRef<Path>,
{
Ok(Self(WavInnerTag {
data: logic::read::wav(File::open(path)?)?
}))
Ok(Self {
inner: WavInnerTag {
data: logic::read::wav(File::open(path)?)?,
},
#[cfg(feature = "duration")]
duration: None,
})
}
}
@ -92,7 +99,7 @@ impl<'a> From<&'a WavTag> for AnyTag<'a> {
impl WavTag {
fn get_value(&self, key: &str) -> Option<&str> {
self.0
self.inner
.data
.as_ref()
.unwrap()
@ -110,15 +117,15 @@ impl WavTag {
where
V: Into<String>,
{
let mut data = self.0.data.clone().unwrap();
let mut data = self.inner.data.clone().unwrap();
let _ = data.insert(key.to_string(), val.into());
self.0.data = Some(data);
self.inner.data = Some(data);
}
fn remove_key(&mut self, key: &str) {
let mut data = self.0.data.clone().unwrap();
let mut data = self.inner.data.clone().unwrap();
data.retain(|k, _| k != key);
self.0.data = Some(data);
self.inner.data = Some(data);
}
}
@ -266,7 +273,7 @@ impl AudioTagEdit for WavTag {
impl AudioTagWrite for WavTag {
fn write_to(&self, file: &mut File) -> Result<()> {
if let Some(data) = self.0.data.clone() {
if let Some(data) = self.inner.data.clone() {
let mut chunk = Vec::new();
chunk.extend(riff::LIST_ID.value.iter());

View file

@ -17,6 +17,9 @@ pub enum Error {
FlacTag(#[from] metaflac::Error),
#[error(transparent)]
Id3Tag(#[from] id3::Error),
#[cfg(feature = "duration")]
#[error(transparent)]
MP3Duration(#[from] mp3_duration::MP3DurationError),
#[error(transparent)]
Mp4Tag(#[from] mp4ameta::Error),
#[error(transparent)]

View file

@ -3,11 +3,19 @@
macro_rules! impl_tag {
($tag:ident, $inner:ident, $tag_type:expr) => {
#[doc(hidden)]
pub struct $tag($inner);
pub struct $tag {
inner: $inner,
#[cfg(feature = "duration")]
duration: Option<Duration>,
}
impl Default for $tag {
fn default() -> Self {
Self($inner::default())
Self {
inner: $inner::default(),
#[cfg(feature = "duration")]
duration: None,
}
}
}
@ -39,14 +47,18 @@ macro_rules! impl_tag {
// From wrapper to inner (same type)
impl From<$tag> for $inner {
fn from(inp: $tag) -> Self {
inp.0
inp.inner
}
}
// From inner to wrapper (same type)
impl From<$inner> for $tag {
fn from(inp: $inner) -> Self {
Self(inp)
Self {
inner: inp,
#[cfg(feature = "duration")]
duration: None,
}
}
}

View file

@ -85,7 +85,6 @@ pub trait AudioTagEdit {
fn total_discs(&self) -> Option<u32>;
fn set_total_discs(&mut self, total_discs: u32);
fn remove_total_discs(&mut self);
}
pub trait AudioTagWrite {

View file

@ -1,4 +1,6 @@
use crate::Album;
#[cfg(feature = "duration")]
use std::time::Duration;
/// The tag returned from `read_from_path`
#[derive(Default, Debug)]
@ -14,7 +16,7 @@ pub struct AnyTag<'a> {
pub disc_number: Option<u32>,
pub total_discs: Option<u32>,
#[cfg(feature = "duration")]
pub duration_ms: Option<u32>,
pub duration: Option<Duration>,
}
impl<'a> AnyTag<'a> {
@ -75,8 +77,8 @@ impl<'a> AnyTag<'a> {
}
#[cfg(feature = "duration")]
/// Returns `duration`
pub fn duration(&self) -> Option<u32> {
self.duration_ms
pub fn duration(&self) -> Option<Duration> {
self.duration
}
}