mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-11-10 06:34:18 +00:00
Merge branch 'main' of https://github.com/TianyiShi2001/audiotags into main
This commit is contained in:
commit
7eb57f1874
6 changed files with 46 additions and 81 deletions
|
@ -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"}
|
|
@ -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> {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
47
src/lib.rs
47
src/lib.rs
|
@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
Loading…
Reference in a new issue