This commit is contained in:
Tianyi 2020-10-27 13:09:41 +00:00
commit 7eb57f1874
6 changed files with 46 additions and 81 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "audiotags" name = "audiotags"
version = "0.2.7" version = "0.2.71"
authors = ["Tianyi <ShiTianyi2001@outlook.com>"] authors = ["Tianyi <ShiTianyi2001@outlook.com>"]
edition = "2018" edition = "2018"
description = "Unified IO for different types of audio metadata" description = "Unified IO for different types of audio metadata"
@ -13,6 +13,5 @@ repository = "https://github.com/TianyiShi2001/audiotags"
id3 = "0.5.1" id3 = "0.5.1"
mp4ameta = "0.6" mp4ameta = "0.6"
metaflac = "0.2" metaflac = "0.2"
beef = "0.4.4"
thiserror = "1.0.21" thiserror = "1.0.21"
audiotags-dev-macro = {path = "./audiotags-dev-macro", version = "0.1.1"} audiotags-dev-macro = {path = "./audiotags-dev-macro", version = "0.1.1"}

View file

@ -39,15 +39,11 @@ impl<'a> From<AnyTag<'a>> for FlacTag {
impl<'a> From<&'a FlacTag> for AnyTag<'a> { impl<'a> From<&'a FlacTag> for AnyTag<'a> {
fn from(inp: &'a FlacTag) -> Self { fn from(inp: &'a FlacTag) -> Self {
let mut t = Self::default(); let mut t = Self::default();
t.title = inp.title().map(Cow::borrowed); t.title = inp.title();
t.artists = inp t.artists = inp.artists();
.artists()
.map(|i| i.into_iter().map(Cow::borrowed).collect::<Vec<_>>());
t.year = inp.year(); t.year = inp.year();
t.album_title = inp.album_title().map(Cow::borrowed); t.album_title = inp.album_title();
t.album_artists = inp t.album_artists = inp.album_artists();
.album_artists()
.map(|i| i.into_iter().map(Cow::borrowed).collect::<Vec<_>>());
t.album_cover = inp.album_cover(); t.album_cover = inp.album_cover();
t.track_number = inp.track_number(); t.track_number = inp.track_number();
t.total_tracks = inp.total_tracks(); t.total_tracks = inp.total_tracks();
@ -130,7 +126,7 @@ impl AudioTag for FlacTag {
.next() .next()
.and_then(|pic| { .and_then(|pic| {
Some(Picture { Some(Picture {
data: Cow::borrowed(&pic.data), data: &pic.data,
mime_type: (pic.mime_type.as_str()).try_into().ok()?, mime_type: (pic.mime_type.as_str()).try_into().ok()?,
}) })
}) })
@ -140,7 +136,7 @@ impl AudioTag for FlacTag {
let mime = String::from(cover.mime_type); let mime = String::from(cover.mime_type);
let picture_type = metaflac::block::PictureType::CoverFront; let picture_type = metaflac::block::PictureType::CoverFront;
self.inner self.inner
.add_picture(mime, picture_type, cover.data.into_owned()); .add_picture(mime, picture_type, (cover.data).to_owned());
} }
fn track_number(&self) -> Option<u16> { fn track_number(&self) -> Option<u16> {

View file

@ -10,15 +10,11 @@ impl<'a> From<&'a Id3v2Tag> for AnyTag<'a> {
Self { Self {
config: inp.config.clone(), config: inp.config.clone(),
title: inp.title().map(Cow::borrowed), title: inp.title(),
artists: inp artists: inp.artists(),
.artists()
.map(|i| i.into_iter().map(Cow::borrowed).collect::<Vec<_>>()),
year: inp.year(), year: inp.year(),
album_title: inp.album_title().map(Cow::borrowed), album_title: inp.album_title(),
album_artists: inp album_artists: inp.album_artists(),
.album_artists()
.map(|i| i.into_iter().map(Cow::borrowed).collect::<Vec<_>>()),
album_cover: inp.album_cover(), album_cover: inp.album_cover(),
track_number: inp.track_number(), track_number: inp.track_number(),
total_tracks: inp.total_tracks(), total_tracks: inp.total_tracks(),
@ -75,21 +71,7 @@ impl<'a> std::convert::TryFrom<&'a id3::frame::Picture> for Picture<'a> {
} = inp; } = inp;
let mime_type: MimeType = mime_type.as_str().try_into()?; let mime_type: MimeType = mime_type.as_str().try_into()?;
Ok(Self { Ok(Self {
data: Cow::borrowed(&data), data: &data,
mime_type,
})
}
}
impl<'a> std::convert::TryFrom<id3::frame::Picture> for Picture<'a> {
type Error = crate::Error;
fn try_from(inp: id3::frame::Picture) -> crate::Result<Self> {
let id3::frame::Picture {
mime_type, data, ..
} = inp;
let mime_type: MimeType = mime_type.as_str().try_into()?;
Ok(Self {
data: Cow::owned(data),
mime_type, mime_type,
}) })
} }
@ -154,7 +136,7 @@ impl AudioTag for Id3v2Tag {
.next() .next()
.and_then(|pic| { .and_then(|pic| {
Some(Picture { Some(Picture {
data: Cow::borrowed(&pic.data), data: &pic.data,
mime_type: (pic.mime_type.as_str()).try_into().ok()?, mime_type: (pic.mime_type.as_str()).try_into().ok()?,
}) })
}) })
@ -165,7 +147,7 @@ impl AudioTag for Id3v2Tag {
mime_type: String::from(cover.mime_type), mime_type: String::from(cover.mime_type),
picture_type: id3::frame::PictureType::CoverFront, picture_type: id3::frame::PictureType::CoverFront,
description: "".to_owned(), description: "".to_owned(),
data: cover.data.into_owned(), data: cover.data.to_owned(),
}); });
} }
fn remove_album_cover(&mut self) { fn remove_album_cover(&mut self) {

View file

@ -38,8 +38,6 @@ use std::convert::From;
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
use beef::lean::Cow;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -175,51 +173,44 @@ impl From<MimeType> for String {
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct Picture<'a> { pub struct Picture<'a> {
pub data: Cow<'a, [u8]>, pub data: &'a [u8],
pub mime_type: MimeType, pub mime_type: MimeType,
} }
impl<'a> Picture<'a> { impl<'a> Picture<'a> {
pub fn new(data: &'a [u8], mime_type: MimeType) -> Self { pub fn new(data: &'a [u8], mime_type: MimeType) -> Self {
Self { Self { data, mime_type }
data: Cow::borrowed(data),
mime_type,
}
} }
} }
/// A struct for representing an album for convinience. /// A struct for representing an album for convinience.
#[derive(Debug)] #[derive(Debug)]
pub struct Album<'a> { pub struct Album<'a> {
pub title: Cow<'a, str>, pub title: &'a str,
pub artist: Option<Cow<'a, str>>, pub artist: Option<&'a str>,
pub cover: Option<Picture<'a>>, pub cover: Option<Picture<'a>>,
} }
impl<'a> Album<'a> { impl<'a> Album<'a> {
pub fn with_title(title: impl Into<String>) -> Self { pub fn with_title(title: &'a str) -> Self {
Self { Self {
title: Cow::owned(title.into()), title: title,
artist: None, artist: None,
cover: None, cover: None,
} }
} }
pub fn and_artist(mut self, artist: impl Into<String>) -> Self { pub fn and_artist(mut self, artist: &'a str) -> Self {
self.artist = Some(Cow::owned(artist.into())); self.artist = Some(artist);
self self
} }
pub fn and_cover(mut self, cover: Picture<'a>) -> Self { pub fn and_cover(mut self, cover: Picture<'a>) -> Self {
self.cover = Some(cover); self.cover = Some(cover);
self self
} }
pub fn with_all( pub fn with_all(title: &'a str, artist: &'a str, cover: Picture<'a>) -> Self {
title: impl Into<String>,
artist: impl Into<String>,
cover: Picture<'a>,
) -> Self {
Self { Self {
title: Cow::owned(title.into()), title,
artist: Some(Cow::owned(artist.into())), artist: Some(artist),
cover: Some(cover), cover: Some(cover),
} }
} }
@ -228,11 +219,11 @@ impl<'a> Album<'a> {
#[derive(Default)] #[derive(Default)]
pub struct AnyTag<'a> { pub struct AnyTag<'a> {
pub config: Config, pub config: Config,
pub title: Option<Cow<'a, str>>, pub title: Option<&'a str>,
pub artists: Option<Vec<Cow<'a, str>>>, // ? iterator pub artists: Option<Vec<&'a str>>, // ? iterator
pub year: Option<i32>, pub year: Option<i32>,
pub album_title: Option<Cow<'a, str>>, pub album_title: Option<&'a str>,
pub album_artists: Option<Vec<Cow<'a, str>>>, // ? iterator pub album_artists: Option<Vec<&'a str>>, // ? iterator
pub album_cover: Option<Picture<'a>>, pub album_cover: Option<Picture<'a>>,
pub track_number: Option<u16>, pub track_number: Option<u16>,
pub total_tracks: Option<u16>, pub total_tracks: Option<u16>,
@ -244,7 +235,7 @@ impl AnyTag<'_> {
pub fn title(&self) -> Option<&str> { pub fn title(&self) -> Option<&str> {
self.title.as_deref() self.title.as_deref()
} }
pub fn artists(&self) -> Option<&[Cow<str>]> { pub fn artists(&self) -> Option<&[&str]> {
self.artists.as_deref() self.artists.as_deref()
} }
pub fn year(&self) -> Option<i32> { pub fn year(&self) -> Option<i32> {
@ -253,7 +244,7 @@ impl AnyTag<'_> {
pub fn album_title(&self) -> Option<&str> { pub fn album_title(&self) -> Option<&str> {
self.album_title.as_deref() self.album_title.as_deref()
} }
pub fn album_artists(&self) -> Option<&[Cow<str>]> { pub fn album_artists(&self) -> Option<&[&str]> {
self.album_artists.as_deref() self.album_artists.as_deref()
} }
pub fn track_number(&self) -> Option<u16> { pub fn track_number(&self) -> Option<u16> {
@ -316,8 +307,8 @@ pub trait AudioTag: AudioTagCommon {
fn album(&self) -> Option<Album<'_>> { fn album(&self) -> Option<Album<'_>> {
self.album_title().map(|title| Album { self.album_title().map(|title| Album {
title: Cow::borrowed(title), title,
artist: self.album_artist().map(Cow::borrowed), artist: self.album_artist(),
cover: self.album_cover(), cover: self.album_cover(),
}) })
} }

View file

@ -7,15 +7,13 @@ impl_tag!(Mp4Tag, InnerTag);
impl<'a> From<&'a Mp4Tag> for AnyTag<'a> { impl<'a> From<&'a Mp4Tag> for AnyTag<'a> {
fn from(inp: &'a Mp4Tag) -> Self { fn from(inp: &'a Mp4Tag) -> Self {
let title = inp.title().map(Cow::borrowed); let title = inp.title();
let artists = inp let artists = inp.artists().map(|i| i.into_iter().collect::<Vec<_>>());
.artists()
.map(|i| i.into_iter().map(Cow::borrowed).collect::<Vec<_>>());
let year = inp.year(); let year = inp.year();
let album_title = inp.album_title().map(Cow::borrowed); let album_title = inp.album_title();
let album_artists = inp let album_artists = inp
.album_artists() .album_artists()
.map(|i| i.into_iter().map(Cow::borrowed).collect::<Vec<_>>()); .map(|i| i.into_iter().collect::<Vec<_>>());
let album_cover = inp.album_cover(); let album_cover = inp.album_cover();
let (a, b) = inp.track(); let (a, b) = inp.track();
let track_number = a; let track_number = a;
@ -47,11 +45,11 @@ impl<'a> From<AnyTag<'a>> for Mp4Tag {
let mut t = mp4ameta::Tag::default(); let mut t = mp4ameta::Tag::default();
inp.title().map(|v| t.set_title(v)); inp.title().map(|v| t.set_title(v));
inp.artists() inp.artists()
.map(|i| i.iter().for_each(|a| t.add_artist(a.as_ref()))); .map(|i| i.iter().for_each(|&a| t.add_artist(a)));
inp.year.map(|v| t.set_year(v.to_string())); inp.year.map(|v| t.set_year(v.to_string()));
inp.album_title().map(|v| t.set_album(v)); inp.album_title().map(|v| t.set_album(v));
inp.album_artists() inp.album_artists()
.map(|i| i.iter().for_each(|a| t.add_album_artist(a.as_ref()))); .map(|i| i.iter().for_each(|&a| t.add_album_artist(a)));
inp.track_number().map(|v| t.set_track_number(v)); inp.track_number().map(|v| t.set_track_number(v));
inp.total_tracks().map(|v| t.set_total_tracks(v)); inp.total_tracks().map(|v| t.set_total_tracks(v));
inp.disc_number().map(|v| t.set_disc_number(v)); inp.disc_number().map(|v| t.set_disc_number(v));
@ -67,11 +65,11 @@ impl<'a> std::convert::TryFrom<&'a mp4ameta::Data> for Picture<'a> {
fn try_from(inp: &'a mp4ameta::Data) -> crate::Result<Self> { fn try_from(inp: &'a mp4ameta::Data) -> crate::Result<Self> {
Ok(match *inp { Ok(match *inp {
mp4ameta::Data::Png(ref data) => Self { mp4ameta::Data::Png(ref data) => Self {
data: Cow::borrowed(data), data,
mime_type: MimeType::Png, mime_type: MimeType::Png,
}, },
mp4ameta::Data::Jpeg(ref data) => Self { mp4ameta::Data::Jpeg(ref data) => Self {
data: Cow::borrowed(data), data,
mime_type: MimeType::Jpeg, mime_type: MimeType::Jpeg,
}, },
_ => return Err(crate::Error::NotAPicture), _ => return Err(crate::Error::NotAPicture),
@ -148,11 +146,11 @@ impl AudioTag for Mp4Tag {
use mp4ameta::Data::*; use mp4ameta::Data::*;
self.inner.artwork().and_then(|data| match data { self.inner.artwork().and_then(|data| match data {
Jpeg(d) => Some(Picture { Jpeg(d) => Some(Picture {
data: Cow::borrowed(d), data: d,
mime_type: MimeType::Jpeg, mime_type: MimeType::Jpeg,
}), }),
Png(d) => Some(Picture { Png(d) => Some(Picture {
data: Cow::borrowed(d), data: d,
mime_type: MimeType::Png, mime_type: MimeType::Png,
}), }),
_ => None, _ => None,
@ -161,8 +159,8 @@ impl AudioTag for Mp4Tag {
fn set_album_cover(&mut self, cover: Picture) { fn set_album_cover(&mut self, cover: Picture) {
self.remove_album_cover(); self.remove_album_cover();
self.inner.add_artwork(match cover.mime_type { self.inner.add_artwork(match cover.mime_type {
MimeType::Png => mp4ameta::Data::Png(cover.data.into_owned()), MimeType::Png => mp4ameta::Data::Png(cover.data.to_owned()),
MimeType::Jpeg => mp4ameta::Data::Jpeg(cover.data.into_owned()), MimeType::Jpeg => mp4ameta::Data::Jpeg(cover.data.to_owned()),
_ => panic!("Only png and jpeg are supported in m4a"), _ => panic!("Only png and jpeg are supported in m4a"),
}); });
} }

View file

@ -1,5 +1,4 @@
use audiotags::{MimeType, Picture, Tag}; use audiotags::{MimeType, Picture, Tag};
use beef::lean::Cow;
macro_rules! test_file { macro_rules! test_file {
( $function:ident, $file:expr ) => { ( $function:ident, $file:expr ) => {
@ -38,7 +37,7 @@ macro_rules! test_file {
let cover = Picture { let cover = Picture {
mime_type: MimeType::Jpeg, mime_type: MimeType::Jpeg,
data: Cow::owned(vec![0u8; 10]), data: &vec![0u8; 10],
}; };
tags.set_album_cover(cover.clone()); tags.set_album_cover(cover.clone());