From 3b1280fcee8568c91a1a801e9c79a5f8cd6c80eb Mon Sep 17 00:00:00 2001 From: Tianyi Date: Mon, 26 Oct 2020 15:37:10 +0000 Subject: [PATCH] cow --- Cargo.toml | 3 +- src/flac_tag.rs | 344 +++++++++++++++++------------------ src/id3_tag.rs | 363 +++++++++++++++++++++++-------------- src/lib.rs | 467 +++++++++++++++++++++++++++++++----------------- src/mp4_tag.rs | 334 ++++++++++++++++++++-------------- tests/conv.rs | 0 tests/io.rs | 92 +++++----- 7 files changed, 948 insertions(+), 655 deletions(-) create mode 100644 tests/conv.rs diff --git a/Cargo.toml b/Cargo.toml index b778e414..2f36204a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "audiotags" -version = "0.1.3" +version = "0.1.5" authors = ["Tianyi "] edition = "2018" description = "Unified IO for different types of audio metadata" @@ -14,3 +14,4 @@ id3 = "0.5.1" mp4ameta = "0.5.1" strum = {version = "0.19.5", features = ["derive"]} metaflac = "0.2" +beef = "0.4.4" diff --git a/src/flac_tag.rs b/src/flac_tag.rs index 22716327..86286491 100644 --- a/src/flac_tag.rs +++ b/src/flac_tag.rs @@ -1,185 +1,185 @@ -use super::*; -use metaflac; +// use super::*; +// use metaflac; -pub(crate) struct FlacTag { - inner: metaflac::Tag, -} +// pub(crate) struct FlacTag { +// inner: metaflac::Tag, +// } -impl FlacTag { - pub fn read_from_path(path: impl AsRef) -> Result { - Ok(Self { - inner: metaflac::Tag::read_from_path(path)?, - }) - } - pub fn get_first(&self, key: &str) -> Option<&str> { - if let Some(Some(v)) = self.inner.vorbis_comments().map(|c| c.get(key)) { - if !v.is_empty() { - Some(v[0].as_str()) - } else { - None - } - } else { - None - } - } - pub fn set_first(&mut self, key: &str, val: &str) { - self.inner.vorbis_comments_mut().set(key, vec![val]); - } - pub fn remove(&mut self, k: &str) { - self.inner.vorbis_comments_mut().comments.remove(k); - } -} +// impl FlacTag { +// pub fn read_from_path(path: impl AsRef) -> Result { +// Ok(Self { +// inner: metaflac::Tag::read_from_path(path)?, +// }) +// } +// pub fn get_first(&self, key: &str) -> Option<&str> { +// if let Some(Some(v)) = self.inner.vorbis_comments().map(|c| c.get(key)) { +// if !v.is_empty() { +// Some(v[0].as_str()) +// } else { +// None +// } +// } else { +// None +// } +// } +// pub fn set_first(&mut self, key: &str, val: &str) { +// self.inner.vorbis_comments_mut().set(key, vec![val]); +// } +// pub fn remove(&mut self, k: &str) { +// self.inner.vorbis_comments_mut().comments.remove(k); +// } +// } -impl AudioTagsIo for FlacTag { - fn title(&self) -> Option<&str> { - self.get_first("TITLE") - } - fn set_title(&mut self, title: &str) { - self.set_first("TITLE", title); - } +// impl AudioTagsIo for FlacTag { +// fn title(&self) -> Option<&str> { +// self.get_first("TITLE") +// } +// fn set_title(&mut self, title: &str) { +// self.set_first("TITLE", title); +// } - fn artist(&self) -> Option<&str> { - self.get_first("ARTIST") - } - fn set_artist(&mut self, artist: &str) { - self.set_first("ARTIST", artist) - } +// fn artist(&self) -> Option<&str> { +// self.get_first("ARTIST") +// } +// fn set_artist(&mut self, artist: &str) { +// self.set_first("ARTIST", artist) +// } - fn year(&self) -> Option { - if let Some(Ok(y)) = self - .get_first("DATE") - .map(|s| s.chars().take(4).collect::().parse::()) - { - Some(y) - } else if let Some(Ok(y)) = self.get_first("YEAR").map(|s| s.parse::()) { - Some(y) - } else { - None - } - } - fn set_year(&mut self, year: i32) { - self.set_first("DATE", &year.to_string()); - self.set_first("YEAR", &year.to_string()); - } +// fn year(&self) -> Option { +// if let Some(Ok(y)) = self +// .get_first("DATE") +// .map(|s| s.chars().take(4).collect::().parse::()) +// { +// Some(y) +// } else if let Some(Ok(y)) = self.get_first("YEAR").map(|s| s.parse::()) { +// Some(y) +// } else { +// None +// } +// } +// fn set_year(&mut self, year: i32) { +// self.set_first("DATE", &year.to_string()); +// self.set_first("YEAR", &year.to_string()); +// } - fn album_title(&self) -> Option<&str> { - self.get_first("ALBUM") - } - fn set_album_title(&mut self, title: &str) { - self.set_first("ALBUM", title) - } +// fn album_title(&self) -> Option<&str> { +// self.get_first("ALBUM") +// } +// fn set_album_title(&mut self, title: &str) { +// self.set_first("ALBUM", title) +// } - fn album_artist(&self) -> Option<&str> { - self.get_first("ALBUMARTIST") - } - fn set_album_artist(&mut self, v: &str) { - self.set_first("ALBUMARTIST", v) - } +// fn album_artist(&self) -> Option<&str> { +// self.get_first("ALBUMARTIST") +// } +// fn set_album_artist(&mut self, v: &str) { +// self.set_first("ALBUMARTIST", v) +// } - fn album_cover(&self) -> Option { - if let Some(Ok(pic)) = self - .inner - .pictures() - .filter(|&pic| matches!(pic.picture_type, metaflac::block::PictureType::CoverFront)) - .next() - .map(|pic| Picture::try_with_mime(pic.data.clone(), &pic.mime_type)) - { - Some(pic) - } else { - None - } - } - fn set_album_cover(&mut self, cover: Picture) { - self.remove_album_cover(); - let mime = String::from(cover.mime_type); - let picture_type = metaflac::block::PictureType::CoverFront; - self.inner.add_picture(mime, picture_type, cover.data); - } +// fn album_cover(&self) -> Option { +// if let Some(Ok(pic)) = self +// .inner +// .pictures() +// .filter(|&pic| matches!(pic.picture_type, metaflac::block::PictureType::CoverFront)) +// .next() +// .map(|pic| Picture::try_with_mime(pic.data.clone(), &pic.mime_type)) +// { +// Some(pic) +// } else { +// None +// } +// } +// fn set_album_cover(&mut self, cover: Picture) { +// self.remove_album_cover(); +// let mime = String::from(cover.mime_type); +// let picture_type = metaflac::block::PictureType::CoverFront; +// self.inner.add_picture(mime, picture_type, cover.data); +// } - fn track_number(&self) -> Option { - if let Some(Ok(n)) = self.get_first("TRACKNUMBER").map(|x| x.parse::()) { - Some(n) - } else { - None - } - } - fn set_track_number(&mut self, v: u16) { - self.set_first("TRACKNUMBER", &v.to_string()) - } +// fn track_number(&self) -> Option { +// if let Some(Ok(n)) = self.get_first("TRACKNUMBER").map(|x| x.parse::()) { +// Some(n) +// } else { +// None +// } +// } +// fn set_track_number(&mut self, v: u16) { +// self.set_first("TRACKNUMBER", &v.to_string()) +// } - // ! not standard - fn total_tracks(&self) -> Option { - if let Some(Ok(n)) = self.get_first("TOTALTRACKS").map(|x| x.parse::()) { - Some(n) - } else { - None - } - } - fn set_total_tracks(&mut self, v: u16) { - self.set_first("TOTALTRACKS", &v.to_string()) - } +// // ! not standard +// fn total_tracks(&self) -> Option { +// if let Some(Ok(n)) = self.get_first("TOTALTRACKS").map(|x| x.parse::()) { +// Some(n) +// } else { +// None +// } +// } +// fn set_total_tracks(&mut self, v: u16) { +// self.set_first("TOTALTRACKS", &v.to_string()) +// } - fn disc_number(&self) -> Option { - if let Some(Ok(n)) = self.get_first("DISCNUMBER").map(|x| x.parse::()) { - Some(n) - } else { - None - } - } - fn set_disc_number(&mut self, v: u16) { - self.set_first("DISCNUMBER", &v.to_string()) - } +// fn disc_number(&self) -> Option { +// if let Some(Ok(n)) = self.get_first("DISCNUMBER").map(|x| x.parse::()) { +// Some(n) +// } else { +// None +// } +// } +// fn set_disc_number(&mut self, v: u16) { +// self.set_first("DISCNUMBER", &v.to_string()) +// } - // ! not standard - fn total_discs(&self) -> Option { - if let Some(Ok(n)) = self.get_first("TOTALDISCS").map(|x| x.parse::()) { - Some(n) - } else { - None - } - } - fn set_total_discs(&mut self, v: u16) { - self.set_first("TOTALDISCS", &v.to_string()) - } +// // ! not standard +// fn total_discs(&self) -> Option { +// if let Some(Ok(n)) = self.get_first("TOTALDISCS").map(|x| x.parse::()) { +// Some(n) +// } else { +// None +// } +// } +// fn set_total_discs(&mut self, v: u16) { +// self.set_first("TOTALDISCS", &v.to_string()) +// } - fn remove_title(&mut self) { - self.remove("TITLE"); - } - fn remove_artist(&mut self) { - self.remove("ARTIST"); - } - fn remove_year(&mut self) { - self.remove("YEAR"); - self.remove("DATE"); - } - fn remove_album_title(&mut self) { - self.remove("ALBUM"); - } - fn remove_album_artist(&mut self) { - self.remove("ALBUMARTIST"); - } - fn remove_album_cover(&mut self) { - self.inner - .remove_picture_type(metaflac::block::PictureType::CoverFront) - } - fn remove_track_number(&mut self) { - self.remove("TRACKNUMBER"); - } - fn remove_total_tracks(&mut self) { - self.remove("TOTALTRACKS"); - } - fn remove_disc_number(&mut self) { - self.remove("DISCNUMBER"); - } - fn remove_total_discs(&mut self) { - self.remove("TOTALDISCS"); - } - fn write_to(&mut self, file: &mut File) -> Result<(), BoxedError> { - self.inner.write_to(file)?; - Ok(()) - } - fn write_to_path(&mut self, path: &str) -> Result<(), BoxedError> { - self.inner.write_to_path(path)?; - Ok(()) - } -} +// fn remove_title(&mut self) { +// self.remove("TITLE"); +// } +// fn remove_artist(&mut self) { +// self.remove("ARTIST"); +// } +// fn remove_year(&mut self) { +// self.remove("YEAR"); +// self.remove("DATE"); +// } +// fn remove_album_title(&mut self) { +// self.remove("ALBUM"); +// } +// fn remove_album_artist(&mut self) { +// self.remove("ALBUMARTIST"); +// } +// fn remove_album_cover(&mut self) { +// self.inner +// .remove_picture_type(metaflac::block::PictureType::CoverFront) +// } +// fn remove_track_number(&mut self) { +// self.remove("TRACKNUMBER"); +// } +// fn remove_total_tracks(&mut self) { +// self.remove("TOTALTRACKS"); +// } +// fn remove_disc_number(&mut self) { +// self.remove("DISCNUMBER"); +// } +// fn remove_total_discs(&mut self) { +// self.remove("TOTALDISCS"); +// } +// fn write_to(&mut self, file: &mut File) -> Result<(), BoxedError> { +// self.inner.write_to(file)?; +// Ok(()) +// } +// fn write_to_path(&mut self, path: &str) -> Result<(), BoxedError> { +// self.inner.write_to_path(path)?; +// Ok(()) +// } +// } diff --git a/src/id3_tag.rs b/src/id3_tag.rs index 3ed9031f..2648bc61 100644 --- a/src/id3_tag.rs +++ b/src/id3_tag.rs @@ -1,143 +1,246 @@ use super::*; use id3; -pub(crate) struct Id3Tag { - inner: id3::Tag, -} +pub type Id3Tag = id3::Tag; -impl Id3Tag { - pub fn read_from_path(path: impl AsRef) -> Result { - Ok(Self { - inner: id3::Tag::read_from_path(path)?, - }) - } -} - -impl AudioTagsIo for Id3Tag { - fn title(&self) -> Option<&str> { - self.inner.title() - } - fn set_title(&mut self, title: &str) { - self.inner.set_title(title) - } - fn remove_title(&mut self) { - self.inner.remove_title(); - } - - fn artist(&self) -> Option<&str> { - self.inner.artist() - } - fn set_artist(&mut self, artist: &str) { - self.inner.set_artist(artist) - } - fn remove_artist(&mut self) { - self.inner.remove_artist(); - } - - fn year(&self) -> Option { - self.inner.year() - } - fn set_year(&mut self, year: i32) { - self.inner.set_year(year) - } - fn remove_year(&mut self) { - self.inner.remove("TYER") - // self.inner.remove_year(); // TODO - } - - fn album_title(&self) -> Option<&str> { - self.inner.album() - } - fn set_album_title(&mut self, v: &str) { - self.inner.set_album(v) - } - fn remove_album_title(&mut self) { - self.inner.remove_album(); - } - - fn album_artist(&self) -> Option<&str> { - self.inner.album_artist() - } - fn set_album_artist(&mut self, v: &str) { - self.inner.set_album_artist(v) - } - fn remove_album_artist(&mut self) { - self.inner.remove_album_artist(); - } - - fn album_cover(&self) -> Option { - if let Some(Ok(pic)) = self - .inner +impl<'a> From<&'a id3::Tag> for AnyTag<'a> { + fn from(inp: &'a id3::Tag) -> Self { + let u32tou16 = |x: u32| x as u16; + let mut t = Self::default(); + t.title = inp.title().map(Cow::borrowed); + // artist + t.year = inp.year(); + t.album_title = inp.album().map(Cow::borrowed); + // album_artist + t.album_cover = if let Some(Ok(pic)) = inp .pictures() .filter(|&pic| matches!(pic.picture_type, id3::frame::PictureType::CoverFront)) .next() - .map(|pic| Picture::try_with_mime(pic.data.clone(), &pic.mime_type)) + .map(|pic| Picture::try_from(pic)) { Some(pic) } else { None - } - } - fn set_album_cover(&mut self, cover: Picture) { - self.remove_album_cover(); - self.inner.add_picture(id3::frame::Picture { - mime_type: String::from(cover.mime_type), - picture_type: id3::frame::PictureType::CoverFront, - description: "".to_owned(), - data: cover.data, - }); - } - fn remove_album_cover(&mut self) { - self.inner - .remove_picture_by_type(id3::frame::PictureType::CoverFront); - } - - fn track_number(&self) -> Option { - self.inner.track().map(|x| x as u16) - } - fn set_track_number(&mut self, track: u16) { - self.inner.set_track(track as u32); - } - fn remove_track_number(&mut self) { - self.inner.remove_track(); - } - - fn total_tracks(&self) -> Option { - self.inner.total_tracks().map(|x| x as u16) - } - fn set_total_tracks(&mut self, total_track: u16) { - self.inner.set_total_tracks(total_track as u32); - } - fn remove_total_tracks(&mut self) { - self.inner.remove_total_tracks(); - } - - fn disc_number(&self) -> Option { - self.inner.disc().map(|x| x as u16) - } - fn set_disc_number(&mut self, disc_number: u16) { - self.inner.set_disc(disc_number as u32) - } - fn remove_disc_number(&mut self) { - self.inner.remove_disc(); - } - - fn total_discs(&self) -> Option { - self.inner.total_discs().map(|x| x as u16) - } - fn set_total_discs(&mut self, total_discs: u16) { - self.inner.set_total_discs(total_discs as u32) - } - fn remove_total_discs(&mut self) { - self.inner.remove_total_discs(); - } - - fn write_to(&mut self, file: &mut File) -> Result<(), BoxedError> { - self.inner.write_to(file, id3::Version::Id3v24)?; - Ok(()) - } - fn write_to_path(&mut self, path: &str) -> Result<(), BoxedError> { - self.inner.write_to_path(path, id3::Version::Id3v24)?; - Ok(()) + }; + t.track_number = inp.track().map(u32tou16); + t.total_tracks = inp.total_tracks().map(u32tou16); + t.disc_number = inp.disc().map(u32tou16); + t.total_discs = inp.total_discs().map(u32tou16); + t } } + +impl<'a> From> for id3::Tag { + fn from(inp: AnyTag<'a>) -> Self { + let mut t = id3::Tag::new(); + inp.title().map(|v| t.set_title(v)); + inp.year.map(|v| t.set_year(v)); + inp.album_title().map(|v| t.set_album(v)); + inp.track_number().map(|v| t.set_track(v as u32)); + inp.total_tracks().map(|v| t.set_total_tracks(v as u32)); + inp.disc_number().map(|v| t.set_disc(v as u32)); + inp.total_discs().map(|v| t.set_total_discs(v as u32)); + t + } +} + +impl<'a> std::convert::TryFrom<&'a id3::frame::Picture> for Picture<'a> { + type Error = crate::Error; + fn try_from(inp: &'a id3::frame::Picture) -> Result { + let &id3::frame::Picture { + ref mime_type, + ref data, + .. + } = inp; + let mime_type: MimeType = mime_type.as_str().try_into()?; + Ok(Self { + data: Cow::borrowed(&data), + mime_type, + }) + } +} + +// pub(crate) struct Id3Tag { +// inner: id3::Tag, +// } + +// impl Default for Id3Tag { +// fn default() -> Self { +// Self { +// inner: id3::Tag::default(), +// } +// } +// } + +// impl Id3Tag { +// pub fn new() -> Self { +// Self { +// inner: id3::Tag::default(), +// } +// } +// pub fn read_from_path(path: impl AsRef) -> StdResult { +// Ok(Self { +// inner: id3::Tag::read_from_path(path)?, +// }) +// } +// } + +// impl AudioTagsIo for Id3Tag { +// fn title(&self) -> Option<&str> { +// self.inner.title() +// } +// fn set_title(&mut self, title: &str) { +// self.inner.set_title(title) +// } +// fn remove_title(&mut self) { +// self.inner.remove_title(); +// } + +// fn artist(&self) -> Option<&str> { +// self.inner.artist() +// } +// fn set_artist(&mut self, artist: &str) { +// self.inner.set_artist(artist) +// } +// fn remove_artist(&mut self) { +// self.inner.remove_artist(); +// } + +// fn year(&self) -> Option { +// self.inner.year() +// } +// fn set_year(&mut self, year: i32) { +// self.inner.set_year(year) +// } +// fn remove_year(&mut self) { +// self.inner.remove("TYER") +// // self.inner.remove_year(); // TODO +// } + +// fn album_title(&self) -> Option<&str> { +// self.inner.album() +// } +// fn set_album_title(&mut self, v: &str) { +// self.inner.set_album(v) +// } +// fn remove_album_title(&mut self) { +// self.inner.remove_album(); +// } + +// fn album_artist(&self) -> Option<&str> { +// self.inner.album_artist() +// } +// fn set_album_artist(&mut self, v: &str) { +// self.inner.set_album_artist(v) +// } +// fn remove_album_artist(&mut self) { +// self.inner.remove_album_artist(); +// } + +// fn album_cover(&self) -> Option { +// if let Some(Ok(pic)) = self +// .inner +// .pictures() +// .filter(|&pic| matches!(pic.picture_type, id3::frame::PictureType::CoverFront)) +// .next() +// .map(|pic| Picture::try_with_mime(pic.data.clone(), &pic.mime_type)) +// { +// Some(pic) +// } else { +// None +// } +// } +// fn set_album_cover(&mut self, cover: Picture) { +// self.remove_album_cover(); +// self.inner.add_picture(id3::frame::Picture { +// mime_type: String::from(cover.mime_type), +// picture_type: id3::frame::PictureType::CoverFront, +// description: "".to_owned(), +// data: cover.data, +// }); +// } +// fn remove_album_cover(&mut self) { +// self.inner +// .remove_picture_by_type(id3::frame::PictureType::CoverFront); +// } + +// fn track_number(&self) -> Option { +// self.inner.track().map(|x| x as u16) +// } +// fn set_track_number(&mut self, track: u16) { +// self.inner.set_track(track as u32); +// } +// fn remove_track_number(&mut self) { +// self.inner.remove_track(); +// } + +// fn total_tracks(&self) -> Option { +// self.inner.total_tracks().map(|x| x as u16) +// } +// fn set_total_tracks(&mut self, total_track: u16) { +// self.inner.set_total_tracks(total_track as u32); +// } +// fn remove_total_tracks(&mut self) { +// self.inner.remove_total_tracks(); +// } + +// fn disc_number(&self) -> Option { +// self.inner.disc().map(|x| x as u16) +// } +// fn set_disc_number(&mut self, disc_number: u16) { +// self.inner.set_disc(disc_number as u32) +// } +// fn remove_disc_number(&mut self) { +// self.inner.remove_disc(); +// } + +// fn total_discs(&self) -> Option { +// self.inner.total_discs().map(|x| x as u16) +// } +// fn set_total_discs(&mut self, total_discs: u16) { +// self.inner.set_total_discs(total_discs as u32) +// } +// fn remove_total_discs(&mut self) { +// self.inner.remove_total_discs(); +// } + +// fn write_to(&mut self, file: &mut File) -> Result<(), BoxedError> { +// self.inner.write_to(file, id3::Version::Id3v24)?; +// Ok(()) +// } +// fn write_to_path(&mut self, path: &str) -> Result<(), BoxedError> { +// self.inner.write_to_path(path, id3::Version::Id3v24)?; +// Ok(()) +// } +// } + +// impl<'a> From> for Id3Tag { +// fn from(anytag: AnyTag) -> Self { +// Self { +// inner: anytag.into(), +// } +// } +// } + +// impl From for id3::Tag { +// fn from(anytag: AnyTag) -> Self { +// let mut id3tag = Self::default(); +// anytag +// .artists_as_string(SEP_ARTIST) +// .map(|v| id3tag.set_artist(v)); +// anytag.year().map(|v| id3tag.set_year(v)); +// anytag.album_title().map(|v| id3tag.set_album(v)); +// anytag +// .album_artists_as_string(SEP_ARTIST) +// .map(|v| id3tag.set_album_artist(v)); +// anytag.track_number().map(|v| id3tag.set_track(v as u32)); +// anytag +// .total_tracks() +// .map(|v| id3tag.set_total_tracks(v as u32)); +// anytag.disc_number().map(|v| id3tag.set_disc(v as u32)); +// anytag +// .total_discs() +// .map(|v| id3tag.set_total_discs(v as u32)); +// id3tag +// } +// } diff --git a/src/lib.rs b/src/lib.rs index 3f989962..3e03cbb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,10 +41,10 @@ //! ``` mod id3_tag; -use id3_tag::Id3Tag; +pub use id3_tag::Id3Tag; mod flac_tag; mod mp4_tag; -use flac_tag::FlacTag; +// use flac_tag::FlacTag; use mp4_tag::Mp4Tag; use std::convert::From; @@ -52,13 +52,22 @@ use std::fs::File; use std::path::Path; use strum::Display; +use beef::lean::Cow; + +use std::convert::{TryFrom, TryInto}; + type BoxedError = Box; #[derive(Debug, Display)] pub enum Error { UnsupportedFormat(String), + UnsupportedMimeType(String), + NotAPicture, } +pub type StdResult = std::result::Result; +pub type Result = std::result::Result; + impl std::error::Error for Error {} #[derive(Clone, Copy, Debug)] @@ -82,12 +91,13 @@ pub enum TagType { Mp4, } +#[rustfmt::skip] impl TagType { - fn try_from_ext(ext: &str) -> Result { + fn try_from_ext(ext: &str) -> StdResult { match ext { - "mp3" => Ok(Self::Id3v2), + "mp3" => Ok(Self::Id3v2), "m4a" | "m4b" | "m4p" | "m4v" | "isom" | "mp4" => Ok(Self::Mp4), - "flac" => Ok(Self::Flac), + "flac" => Ok(Self::Flac), p @ _ => Err(Box::new(Error::UnsupportedFormat(p.to_owned()))), } } @@ -98,52 +108,38 @@ pub struct Tag { tag_type: Option, } -impl Tag { - pub fn with_tag_type(tag_type: TagType) -> Self { - Self { - tag_type: Some(tag_type), - } - } +// impl Tag { +// pub fn with_tag_type(tag_type: TagType) -> Self { +// Self { +// tag_type: Some(tag_type), +// } +// } - pub fn read_from_path( - &self, - path: impl AsRef, - ) -> Result, BoxedError> { - match self.tag_type.unwrap_or(TagType::try_from_ext( - path.as_ref() - .extension() - .unwrap() - .to_string_lossy() - .to_string() - .to_lowercase() - .as_str(), - )?) { - TagType::Id3v2 => Ok(Box::new(Id3Tag::read_from_path(path)?)), - TagType::Mp4 => Ok(Box::new(Mp4Tag::read_from_path(path)?)), - TagType::Flac => Ok(Box::new(FlacTag::read_from_path(path)?)), - } - } -} +// pub fn read_from_path( +// &self, +// path: impl AsRef, +// ) -> Result, BoxedError> { +// match self.tag_type.unwrap_or(TagType::try_from_ext( +// path.as_ref() +// .extension() +// .unwrap() +// .to_string_lossy() +// .to_string() +// .to_lowercase() +// .as_str(), +// )?) { +// TagType::Id3v2 => Ok(Box::new(Id3Tag::read_from_path(path)?)), +// TagType::Mp4 => Ok(Box::new(Mp4Tag::read_from_path(path)?)), +// TagType::Flac => Ok(Box::new(FlacTag::read_from_path(path)?)), +// } +// } +// } -/// Guesses the audio metadata handler from the file extension, and returns the `Box`ed IO handler. -pub fn read_from_path(path: impl AsRef) -> Result, BoxedError> { - match path - .as_ref() - .extension() - .unwrap() - .to_string_lossy() - .to_string() - .to_lowercase() - .as_str() - { - "mp3" => Ok(Box::new(Id3Tag::read_from_path(path)?)), - "m4a" | "m4b" | "m4p" | "m4v" | "isom" | "mp4" => { - Ok(Box::new(Mp4Tag::read_from_path(path)?)) - } - "flac" => Ok(Box::new(FlacTag::read_from_path(path)?)), - p @ _ => Err(Box::new(Error::UnsupportedFormat(p.to_owned()))), - } -} +// // ? deprecate? +// /// Guesses the audio metadata handler from the file extension, and returns the `Box`ed IO handler. +// pub fn read_from_path(path: impl AsRef) -> Result, BoxedError> { +// Tag::default().read_from_path(path) +// } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum MimeType { @@ -154,6 +150,20 @@ pub enum MimeType { Gif, } +impl TryFrom<&str> for MimeType { + type Error = Error; + fn try_from(inp: &str) -> Result { + Ok(match inp { + "image/jpeg" => MimeType::Jpeg, + "image/png" => MimeType::Png, + "image/tiff" => MimeType::Tiff, + "image/bmp" => MimeType::Bmp, + "image/gif" => MimeType::Gif, + _ => return Err(Error::UnsupportedMimeType(inp.to_owned())), + }) + } +} + impl From for String { fn from(mt: MimeType) -> Self { match mt { @@ -167,127 +177,250 @@ impl From for String { } #[derive(Debug, Clone, Eq, PartialEq)] -pub struct Picture { - pub data: Vec, +pub struct Picture<'a> { + pub data: Cow<'a, [u8]>, pub mime_type: MimeType, } -impl Picture { - pub fn try_with_mime(data: Vec, mime: &str) -> Result { - let mime_type = match mime { - "image/jpeg" => MimeType::Jpeg, - "image/png" => MimeType::Png, - "image/tiff" => MimeType::Tiff, - "image/bmp" => MimeType::Bmp, - "image/gif" => MimeType::Gif, - _ => return Err(()), - }; - Ok(Self { data, mime_type }) - } -} - -#[derive(Debug)] -pub struct Album { - pub title: String, - pub artist: Option, - pub cover: Option, -} - -/// Implementors of this trait are able to read and write audio metadata. -/// -/// Constructor methods e.g. `from_file` should be implemented separately. -pub trait AudioTagsIo { - fn title(&self) -> Option<&str>; - fn set_title(&mut self, title: &str); - fn remove_title(&mut self); - - fn artist(&self) -> Option<&str>; - fn set_artist(&mut self, artist: &str); - fn remove_artist(&mut self); - - fn year(&self) -> Option; - fn set_year(&mut self, year: i32); - fn remove_year(&mut self); - - fn album(&self) -> Option { - self.album_title().map(|title| Album { - title: title.to_owned(), - artist: self.album_artist().map(|x| x.to_owned()), - cover: self.album_cover(), +impl<'a> Picture<'a> { + pub fn try_with_mime(data: Vec, mime: &str) -> Result { + let mime_type: MimeType = mime.try_into()?; + Ok(Self { + data: Cow::owned(data), + mime_type, }) } - fn set_album(&mut self, album: Album) { - self.set_album_title(&album.title); - if let Some(artist) = album.artist { - self.set_album_artist(&artist) - } else { - self.remove_album_artist() - } - if let Some(pic) = album.cover { - self.set_album_cover(pic) - } else { - self.remove_album_cover() - } - } - fn remove_album(&mut self) { - self.remove_album_title(); - self.remove_album_artist(); - self.remove_album_cover(); - } - - fn album_title(&self) -> Option<&str>; - fn set_album_title(&mut self, v: &str); - fn remove_album_title(&mut self); - - fn album_artist(&self) -> Option<&str>; - fn set_album_artist(&mut self, v: &str); - fn remove_album_artist(&mut self); - - fn album_cover(&self) -> Option; - fn set_album_cover(&mut self, cover: Picture); - fn remove_album_cover(&mut self); - - fn track(&self) -> (Option, Option) { - (self.track_number(), self.total_tracks()) - } - fn set_track(&mut self, track: (u16, u16)) { - self.set_track_number(track.0); - self.set_total_tracks(track.1); - } - fn remove_track(&mut self) { - self.remove_track_number(); - self.remove_total_tracks(); - } - - fn track_number(&self) -> Option; - fn set_track_number(&mut self, track_number: u16); - fn remove_track_number(&mut self); - - fn total_tracks(&self) -> Option; - fn set_total_tracks(&mut self, total_track: u16); - fn remove_total_tracks(&mut self); - - fn disc(&self) -> (Option, Option) { - (self.disc_number(), self.total_discs()) - } - fn set_disc(&mut self, disc: (u16, u16)) { - self.set_disc_number(disc.0); - self.set_total_discs(disc.1); - } - fn remove_disc(&mut self) { - self.remove_disc_number(); - self.remove_total_discs(); - } - - fn disc_number(&self) -> Option; - fn set_disc_number(&mut self, disc_number: u16); - fn remove_disc_number(&mut self); - - fn total_discs(&self) -> Option; - fn set_total_discs(&mut self, total_discs: u16); - fn remove_total_discs(&mut self); - - fn write_to(&mut self, file: &mut File) -> Result<(), BoxedError>; - // cannot use impl AsRef - fn write_to_path(&mut self, path: &str) -> Result<(), BoxedError>; } + +/// A struct for representing an album for convinience. +#[derive(Debug)] +pub struct Album<'a> { + pub title: Cow<'a, str>, + pub artist: Option>, + pub cover: Option>, +} + +impl<'a> Album<'a> { + pub fn with_title(title: impl Into) -> Self { + Self { + title: Cow::owned(title.into()), + artist: None, + cover: None, + } + } + pub fn and_artist(mut self, artist: impl Into) -> Self { + self.artist = Some(Cow::owned(artist.into())); + self + } + pub fn and_cover(mut self, cover: Picture<'a>) -> Self { + self.cover = Some(cover); + self + } + pub fn with_all( + title: impl Into, + artist: impl Into, + cover: Picture<'a>, + ) -> Self { + Self { + title: Cow::owned(title.into()), + artist: Some(Cow::owned(artist.into())), + cover: Some(cover), + } + } +} + +const SEP_ARTIST: &'static str = ";"; + +#[derive(Default)] +pub struct AnyTag<'a> { + pub title: Option>, + // pub artists: Option>>, // ? iterator + pub year: Option, + pub album_title: Option>, + // pub album_artists: Option>>, // ? iterator + pub album_cover: Option>, + pub track_number: Option, + pub total_tracks: Option, + pub disc_number: Option, + pub total_discs: Option, +} + +impl<'a> AnyTag<'a> { + pub fn title(&self) -> Option<&str> { + self.title.as_deref() + } + // pub fn artists(&self) -> Option<&[String]> { + // self.artists.as_deref() + // } + pub fn year(&self) -> Option { + self.year + } + pub fn album_title(&self) -> Option<&str> { + self.album_title.as_deref() + } + // pub fn album_artists(&self) -> Option<&[String]> { + // self.album_artists.as_deref() + // } + pub fn track_number(&self) -> Option { + self.track_number + } + pub fn total_tracks(&self) -> Option { + self.total_tracks + } + pub fn disc_number(&self) -> Option { + self.track_number + } + pub fn total_discs(&self) -> Option { + self.total_tracks + } +} + +pub trait TagIo { + fn read_from_path(path: &str) -> StdResult; + fn write_to_path(path: &str) -> StdResult<(), BoxedError>; +} + +// impl<'a> AnyTag<'a> { +// fn read_from_path<> +// } + +fn read_from_path(path: &str) -> StdResult +where + T: TagIo, +{ + T::read_from_path(path) +} + +// Implementors of this trait are able to read and write audio metadata. +// +// Constructor methods e.g. `from_file` should be implemented separately. +// pub trait AudioTagsIo { +// fn title(&self) -> Option>; +// fn set_title(&mut self, title: &str); +// fn remove_title(&mut self); + +// fn artist(&self) -> Option<&str>; +// fn set_artist(&mut self, artist: &str); +// fn remove_artist(&mut self); + +// fn year(&self) -> Option; +// fn set_year(&mut self, year: i32); +// fn remove_year(&mut self); + +// fn album(&self) -> Option { +// self.album_title().map(|title| Album { +// title: title.to_owned(), +// artist: self.album_artist().map(|x| x.to_owned()), +// cover: self.album_cover(), +// }) +// } +// fn set_album(&mut self, album: Album) { +// self.set_album_title(&album.title); +// if let Some(artist) = album.artist { +// self.set_album_artist(&artist) +// } else { +// self.remove_album_artist() +// } +// if let Some(pic) = album.cover { +// self.set_album_cover(pic) +// } else { +// self.remove_album_cover() +// } +// } +// fn remove_album(&mut self) { +// self.remove_album_title(); +// self.remove_album_artist(); +// self.remove_album_cover(); +// } + +// fn album_title(&self) -> Option<&str>; +// fn set_album_title(&mut self, v: &str); +// fn remove_album_title(&mut self); + +// fn album_artist(&self) -> Option<&str>; +// fn set_album_artist(&mut self, v: &str); +// fn remove_album_artist(&mut self); + +// fn album_cover(&self) -> Option; +// fn set_album_cover(&mut self, cover: Picture); +// fn remove_album_cover(&mut self); + +// fn track(&self) -> (Option, Option) { +// (self.track_number(), self.total_tracks()) +// } +// fn set_track(&mut self, track: (u16, u16)) { +// self.set_track_number(track.0); +// self.set_total_tracks(track.1); +// } +// fn remove_track(&mut self) { +// self.remove_track_number(); +// self.remove_total_tracks(); +// } + +// fn track_number(&self) -> Option; +// fn set_track_number(&mut self, track_number: u16); +// fn remove_track_number(&mut self); + +// fn total_tracks(&self) -> Option; +// fn set_total_tracks(&mut self, total_track: u16); +// fn remove_total_tracks(&mut self); + +// fn disc(&self) -> (Option, Option) { +// (self.disc_number(), self.total_discs()) +// } +// fn set_disc(&mut self, disc: (u16, u16)) { +// self.set_disc_number(disc.0); +// self.set_total_discs(disc.1); +// } +// fn remove_disc(&mut self) { +// self.remove_disc_number(); +// self.remove_total_discs(); +// } + +// fn disc_number(&self) -> Option; +// fn set_disc_number(&mut self, disc_number: u16); +// fn remove_disc_number(&mut self); + +// fn total_discs(&self) -> Option; +// fn set_total_discs(&mut self, total_discs: u16); +// fn remove_total_discs(&mut self); + +// fn write_to(&mut self, file: &mut File) -> Result<(), BoxedError>; +// // cannot use impl AsRef +// fn write_to_path(&mut self, path: &str) -> Result<(), BoxedError>; +// } + +// impl AnyTag { +// pub fn artists_as_string(&self, sep: &str) -> Option { +// self.artists().map(|artists| artists.join(sep)) +// } +// pub fn album_artists_as_string(&self, sep: &str) -> Option { +// self.album_artists().map(|artists| artists.join(sep)) +// } +// } + +// #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +// pub enum PictureType { +// Other, +// Icon, +// OtherIcon, +// CoverFront, +// CoverBack, +// Leaflet, +// Media, +// LeadArtist, +// Artist, +// Conductor, +// Band, +// Composer, +// Lyricist, +// RecordingLocation, +// DuringRecording, +// DuringPerformance, +// ScreenCapture, +// BrightFish, +// Illustration, +// BandLogo, +// PublisherLogo, +// Undefined(u8), +// } diff --git a/src/mp4_tag.rs b/src/mp4_tag.rs index 9f0e81e5..745c483b 100644 --- a/src/mp4_tag.rs +++ b/src/mp4_tag.rs @@ -1,157 +1,213 @@ use super::*; use mp4ameta; -pub(crate) struct Mp4Tag { - inner: mp4ameta::Tag, +pub type Mp4Tag = mp4ameta::Tag; + +impl<'a> From<&'a mp4ameta::Tag> for AnyTag<'a> { + fn from(inp: &'a mp4ameta::Tag) -> Self { + let mut t = Self::default(); + t.title = inp.title().map(Cow::borrowed); + // artist + if let Some(Ok(y)) = inp.year().map(|y| y.parse()) { + t.year = Some(y); + } + t.album_title = inp.album().map(Cow::borrowed); + // album_artist + if let Some(Ok(img)) = inp.artwork().map(|a| a.try_into()) { + t.album_cover = Some(img); + } + let (a, b) = inp.track(); + t.track_number = a; + t.total_tracks = b; + let (a, b) = inp.disc(); + t.disc_number = a; + t.total_discs = b; + t + } } -impl Mp4Tag { - pub fn read_from_path(path: impl AsRef) -> Result { - Ok(Self { - inner: mp4ameta::Tag::read_from_path(path)?, +impl<'a> From> for mp4ameta::Tag { + fn from(inp: AnyTag<'a>) -> Self { + let mut t = mp4ameta::Tag::default(); + inp.title().map(|v| t.set_title(v)); + inp.year.map(|v| t.set_year(v.to_string())); + inp.album_title().map(|v| t.set_album(v)); + inp.track_number().map(|v| t.set_track_number(v)); + inp.total_tracks().map(|v| t.set_total_tracks(v)); + inp.disc_number().map(|v| t.set_disc_number(v)); + inp.total_discs().map(|v| t.set_total_discs(v)); + t + } +} + +impl<'a> std::convert::TryFrom<&'a mp4ameta::Data> for Picture<'a> { + type Error = crate::Error; + fn try_from(inp: &'a mp4ameta::Data) -> Result { + Ok(match *inp { + mp4ameta::Data::Png(ref data) => Self { + data: Cow::borrowed(data), + mime_type: MimeType::Png, + }, + mp4ameta::Data::Jpeg(ref data) => Self { + data: Cow::borrowed(data), + mime_type: MimeType::Jpeg, + }, + _ => return Err(Error::NotAPicture), }) } } -impl AudioTagsIo for Mp4Tag { - fn title(&self) -> Option<&str> { - self.inner.title() - } - fn set_title(&mut self, title: &str) { - self.inner.set_title(title) - } +// pub(crate) struct Mp4Tag { +// inner: mp4ameta::Tag, +// } - fn artist(&self) -> Option<&str> { - self.inner.artist() - } - fn set_artist(&mut self, artist: &str) { - self.inner.set_artist(artist) - } +// impl Mp4Tag { +// pub fn read_from_path(path: impl AsRef) -> Result { +// Ok(Self { +// inner: mp4ameta::Tag::read_from_path(path)?, +// }) +// } +// } - fn year(&self) -> Option { - match self.inner.year() { - Some(year) => str::parse(year).ok(), - None => None, - } - } - fn set_year(&mut self, year: i32) { - self.inner.set_year(year.to_string()) - } +// impl AudioTagsIo for Mp4Tag { +// fn title(&self) -> Option<&str> { +// self.inner.title() +// } +// fn set_title(&mut self, title: &str) { +// self.inner.set_title(title) +// } - fn album_title(&self) -> Option<&str> { - self.inner.album() - } - fn set_album_title(&mut self, v: &str) { - self.inner.set_album(v) - } +// fn artist(&self) -> Option<&str> { +// self.inner.artist() +// } +// fn set_artist(&mut self, artist: &str) { +// self.inner.set_artist(artist) +// } - fn album_artist(&self) -> Option<&str> { - self.inner.album_artist() - } - fn set_album_artist(&mut self, v: &str) { - self.inner.set_album_artist(v) - } +// fn year(&self) -> Option { +// match self.inner.year() { +// Some(year) => str::parse(year).ok(), +// None => None, +// } +// } +// fn set_year(&mut self, year: i32) { +// self.inner.set_year(year.to_string()) +// } - fn album_cover(&self) -> Option { - use mp4ameta::Data::*; - if let Some(Some(pic)) = self.inner.artwork().map(|data| match data { - Jpeg(d) => Some(Picture { - data: d.clone(), - mime_type: MimeType::Jpeg, - }), - Png(d) => Some(Picture { - data: d.clone(), - mime_type: MimeType::Png, - }), - _ => None, - }) { - Some(pic) - } else { - None - } - } - fn set_album_cover(&mut self, cover: Picture) { - self.remove_album_cover(); - self.inner.add_artwork(match cover.mime_type { - MimeType::Png => mp4ameta::Data::Png(cover.data), - MimeType::Jpeg => mp4ameta::Data::Jpeg(cover.data), - _ => panic!("Only png and jpeg are supported in m4a"), - }); - } +// fn album_title(&self) -> Option<&str> { +// self.inner.album() +// } +// fn set_album_title(&mut self, v: &str) { +// self.inner.set_album(v) +// } - fn track_number(&self) -> Option { - self.inner.track_number() - } - fn total_tracks(&self) -> Option { - self.inner.total_tracks() - } - fn set_track_number(&mut self, track: u16) { - self.inner.set_track_number(track); - } - fn set_total_tracks(&mut self, total_track: u16) { - self.inner.set_total_tracks(total_track); - } +// fn album_artist(&self) -> Option<&str> { +// self.inner.album_artist() +// } +// fn set_album_artist(&mut self, v: &str) { +// self.inner.set_album_artist(v) +// } - fn disc_number(&self) -> Option { - self.inner.disc_number() - } - fn total_discs(&self) -> Option { - self.inner.total_discs() - } - fn set_disc_number(&mut self, disc_number: u16) { - self.inner.set_disc_number(disc_number) - } - fn set_total_discs(&mut self, total_discs: u16) { - self.inner.set_total_discs(total_discs) - } +// fn album_cover(&self) -> Option { +// use mp4ameta::Data::*; +// if let Some(Some(pic)) = self.inner.artwork().map(|data| match data { +// Jpeg(d) => Some(Picture { +// data: d.clone(), +// mime_type: MimeType::Jpeg, +// }), +// Png(d) => Some(Picture { +// data: d.clone(), +// mime_type: MimeType::Png, +// }), +// _ => None, +// }) { +// Some(pic) +// } else { +// None +// } +// } +// fn set_album_cover(&mut self, cover: Picture) { +// self.remove_album_cover(); +// self.inner.add_artwork(match cover.mime_type { +// MimeType::Png => mp4ameta::Data::Png(cover.data), +// MimeType::Jpeg => mp4ameta::Data::Jpeg(cover.data), +// _ => panic!("Only png and jpeg are supported in m4a"), +// }); +// } - fn remove_title(&mut self) { - self.inner.remove_title(); - } - fn remove_artist(&mut self) { - self.inner.remove_data(mp4ameta::atom::ARTIST); - self.inner.remove_artists(); - } - fn remove_year(&mut self) { - self.inner.remove_year(); - } - fn remove_album_title(&mut self) { - self.inner.remove_album(); - } - fn remove_album_artist(&mut self) { - self.inner.remove_data(mp4ameta::atom::ALBUM_ARTIST); - self.inner.remove_album_artists(); - } - fn remove_album_cover(&mut self) { - self.inner.remove_artwork(); - } - fn remove_track(&mut self) { - self.inner.remove_track(); // faster than removing separately - } - fn remove_track_number(&mut self) { - // self.inner.remove_data(mp4ameta::atom::TRACK_NUMBER); not correct - // TODO: self.inner.remove_track_number(); - } - fn remove_total_tracks(&mut self) { - // TODO: self.inner.remove_total_tracks(); - } - fn remove_disc(&mut self) { - self.inner.remove_disc(); - } - fn remove_disc_number(&mut self) { - // self.inner.remove_data(mp4ameta::atom::DISC_NUMBER); not correct - // TODO: self.inner.remove_disc_number(); - } - fn remove_total_discs(&mut self) { - // TODO: self.inner.remove_total_discs(); - } +// fn track_number(&self) -> Option { +// self.inner.track_number() +// } +// fn total_tracks(&self) -> Option { +// self.inner.total_tracks() +// } +// fn set_track_number(&mut self, track: u16) { +// self.inner.set_track_number(track); +// } +// fn set_total_tracks(&mut self, total_track: u16) { +// self.inner.set_total_tracks(total_track); +// } - fn write_to(&mut self, file: &mut File) -> Result<(), BoxedError> { - self.inner.write_to(file)?; - Ok(()) - } - fn write_to_path(&mut self, path: &str) -> Result<(), BoxedError> { - self.inner.write_to_path(path)?; - Ok(()) - } -} +// fn disc_number(&self) -> Option { +// self.inner.disc_number() +// } +// fn total_discs(&self) -> Option { +// self.inner.total_discs() +// } +// fn set_disc_number(&mut self, disc_number: u16) { +// self.inner.set_disc_number(disc_number) +// } +// fn set_total_discs(&mut self, total_discs: u16) { +// self.inner.set_total_discs(total_discs) +// } + +// fn remove_title(&mut self) { +// self.inner.remove_title(); +// } +// fn remove_artist(&mut self) { +// self.inner.remove_data(mp4ameta::atom::ARTIST); +// self.inner.remove_artists(); +// } +// fn remove_year(&mut self) { +// self.inner.remove_year(); +// } +// fn remove_album_title(&mut self) { +// self.inner.remove_album(); +// } +// fn remove_album_artist(&mut self) { +// self.inner.remove_data(mp4ameta::atom::ALBUM_ARTIST); +// self.inner.remove_album_artists(); +// } +// fn remove_album_cover(&mut self) { +// self.inner.remove_artwork(); +// } +// fn remove_track(&mut self) { +// self.inner.remove_track(); // faster than removing separately +// } +// fn remove_track_number(&mut self) { +// // self.inner.remove_data(mp4ameta::atom::TRACK_NUMBER); not correct +// // TODO: self.inner.remove_track_number(); +// } +// fn remove_total_tracks(&mut self) { +// // TODO: self.inner.remove_total_tracks(); +// } +// fn remove_disc(&mut self) { +// self.inner.remove_disc(); +// } +// fn remove_disc_number(&mut self) { +// // self.inner.remove_data(mp4ameta::atom::DISC_NUMBER); not correct +// // TODO: self.inner.remove_disc_number(); +// } +// fn remove_total_discs(&mut self) { +// // TODO: self.inner.remove_total_discs(); +// } + +// fn write_to(&mut self, file: &mut File) -> Result<(), BoxedError> { +// self.inner.write_to(file)?; +// Ok(()) +// } +// fn write_to_path(&mut self, path: &str) -> Result<(), BoxedError> { +// self.inner.write_to_path(path)?; +// Ok(()) +// } +// } diff --git a/tests/conv.rs b/tests/conv.rs new file mode 100644 index 00000000..e69de29b diff --git a/tests/io.rs b/tests/io.rs index 0c072bad..6a14f1f7 100644 --- a/tests/io.rs +++ b/tests/io.rs @@ -1,54 +1,54 @@ -use audiotags::{MimeType, Picture, Tag}; +// use audiotags::{MimeType, Picture, Tag}; -macro_rules! test_file { - ( $function:ident, $file:expr ) => { - #[test] - fn $function() { - let mut tags = Tag::default().read_from_path($file).unwrap(); - tags.set_title("foo title"); - assert_eq!(tags.title(), Some("foo title")); - tags.remove_title(); - assert!(tags.title().is_none()); - tags.remove_title(); // should not panic +// macro_rules! test_file { +// ( $function:ident, $file:expr ) => { +// #[test] +// fn $function() { +// let mut tags = Tag::default().read_from_path($file).unwrap(); +// tags.set_title("foo title"); +// assert_eq!(tags.title(), Some("foo title")); +// tags.remove_title(); +// assert!(tags.title().is_none()); +// tags.remove_title(); // should not panic - tags.set_artist("foo artist"); - assert_eq!(tags.artist(), Some("foo artist")); - tags.remove_artist(); - assert!(tags.artist().is_none()); - tags.remove_artist(); +// tags.set_artist("foo artist"); +// assert_eq!(tags.artist(), Some("foo artist")); +// tags.remove_artist(); +// assert!(tags.artist().is_none()); +// tags.remove_artist(); - tags.set_year(2020); - assert_eq!(tags.year(), Some(2020)); - tags.remove_year(); - assert!(tags.year().is_none()); - tags.remove_year(); +// tags.set_year(2020); +// assert_eq!(tags.year(), Some(2020)); +// tags.remove_year(); +// assert!(tags.year().is_none()); +// tags.remove_year(); - tags.set_album_title("foo album title"); - assert_eq!(tags.album_title(), Some("foo album title")); - tags.remove_album_title(); - assert!(tags.album_title().is_none()); - tags.remove_album_title(); +// tags.set_album_title("foo album title"); +// assert_eq!(tags.album_title(), Some("foo album title")); +// tags.remove_album_title(); +// assert!(tags.album_title().is_none()); +// tags.remove_album_title(); - tags.set_album_artist("foo album artist"); - assert_eq!(tags.album_artist(), Some("foo album artist")); - tags.remove_album_artist(); - assert!(tags.album_artist().is_none()); - tags.remove_album_artist(); +// tags.set_album_artist("foo album artist"); +// assert_eq!(tags.album_artist(), Some("foo album artist")); +// tags.remove_album_artist(); +// assert!(tags.album_artist().is_none()); +// tags.remove_album_artist(); - let cover = Picture { - mime_type: MimeType::Jpeg, - data: vec![0u8; 10], - }; +// let cover = Picture { +// mime_type: MimeType::Jpeg, +// data: vec![0u8; 10], +// }; - tags.set_album_cover(cover.clone()); - assert_eq!(tags.album_cover(), Some(cover)); - tags.remove_album_cover(); - assert!(tags.album_cover().is_none()); - tags.remove_album_cover(); - } - }; -} +// tags.set_album_cover(cover.clone()); +// assert_eq!(tags.album_cover(), Some(cover)); +// tags.remove_album_cover(); +// assert!(tags.album_cover().is_none()); +// tags.remove_album_cover(); +// } +// }; +// } -test_file!(test_mp3, "assets/a.mp3"); -test_file!(test_m4a, "assets/a.m4a"); -test_file!(test_flac, "assets/a.flac"); +// test_file!(test_mp3, "assets/a.mp3"); +// test_file!(test_m4a, "assets/a.m4a"); +// test_file!(test_flac, "assets/a.flac");