2020-10-25 14:58:50 +00:00
//! This crate makes it easier to parse tags/metadata in audio files of different file types.
//!
2020-10-25 21:39:38 +00:00
//! This crate aims to provide a unified trait for parsers and writers of different audio file formats. This means that you can parse tags in mp3 and m4a files with a single function: `audiotags::read_from_path()` and get fields by directly calling `.album()`, `.artist()` on its result. Without this crate, you would otherwise need to learn different APIs in **id3**, **mp4ameta** crates in order to parse metadata in different file foramts.
2020-10-25 14:58:50 +00:00
//!
//! ## Example
//!
//! ```ignore
//! use audiotags;
//!
//! fn main() {
//! const MP3: &'static str = "a.mp3";
2020-10-25 21:39:38 +00:00
//! let mut tags = audiotags::read_from_path(MP3).unwrap();
//! // without this crate you would call id3::Tag::read_from_path()
2020-10-25 14:58:50 +00:00
//! println!("Title: {:?}", tags.title());
//! println!("Artist: {:?}", tags.artist());
//! tags.set_album_artist("CINDERELLA PROJECT");
//! let album = tags.album().unwrap();
//! println!("Album title and artist: {:?}", (album.title, album.artist));
//! println!("Track: {:?}", tags.track());
//! tags.write_to_path(MP3).unwrap();
//! // Title: Some("お願い!シンデレラ")
//! // Artist: Some("高垣楓、城ヶ崎美嘉、小日向美穂、十時愛梨、川島瑞樹、日野茜、輿水幸子、佐久間まゆ、白坂小梅")
//! // Album title and artist: ("THE IDOLM@STER CINDERELLA GIRLS ANIMATION PROJECT 01 Star!!", Some("CINDERELLA PROJECT"))
//! // Track: (Some(2), Some(4))
//!
//! const M4A: &'static str = "b.m4a";
2020-10-25 21:39:38 +00:00
//! let mut tags = audiotags::read_from_path(M4A).unwrap();
//! // without this crate you would call mp4ameta::Tag::read_from_path()
2020-10-25 14:58:50 +00:00
//! println!("Title: {:?}", tags.title());
//! println!("Artist: {:?}", tags.artist());
//! let album = tags.album().unwrap();
//! println!("Album title and artist: {:?}", (album.title, album.artist));
//! tags.set_total_tracks(4);
//! println!("Track: {:?}", tags.track());
//! tags.write_to_path(M4A).unwrap();
//! // Title: Some("ふわふわ時間")
//! // Artist: Some("桜高軽音部 [平沢唯・秋山澪・田井中律・琴吹紬(CV:豊崎愛生、日笠陽子、佐藤聡美、寿美菜子)]")
//! // Album title and artist: ("ふわふわ時間", Some("桜高軽音部 [平沢唯・秋山澪・田井中律・琴吹紬(CV:豊崎愛生、日笠陽子、佐藤聡美、寿美菜子)]"))
//! // Track: (Some(1), Some(4))
//! }
//! ```
use id3 ;
2020-10-25 21:39:38 +00:00
use metaflac ;
2020-10-25 14:58:50 +00:00
use mp4ameta ;
2020-10-25 21:39:38 +00:00
use std ::convert ::From ;
2020-10-25 14:58:50 +00:00
use std ::fs ::File ;
use std ::path ::Path ;
use strum ::Display ;
type BoxedError = Box < dyn std ::error ::Error > ;
#[ derive(Debug, Display) ]
pub enum Error {
UnsupportedFormat ( String ) ,
}
impl std ::error ::Error for Error { }
/// Guesses the audio metadata handler from the file extension, and returns the `Box`ed IO handler.
2020-10-25 21:39:38 +00:00
pub fn read_from_path ( path : impl AsRef < Path > ) -> Result < Box < dyn AudioTagsIo > , BoxedError > {
2020-10-25 14:58:50 +00:00
match path
. as_ref ( )
. extension ( )
. unwrap ( )
. to_string_lossy ( )
. to_string ( )
. to_lowercase ( )
. as_str ( )
{
2020-10-25 21:39:38 +00:00
" mp3 " = > Ok ( Box ::new ( Id3Tags ::read_from_path ( path ) ? ) ) ,
" m4a " | " m4b " | " m4p " | " m4v " | " isom " | " mp4 " = > {
Ok ( Box ::new ( M4aTags ::read_from_path ( path ) ? ) )
}
" flac " = > Ok ( Box ::new ( FlacTags ::read_from_path ( path ) ? ) ) ,
2020-10-25 14:58:50 +00:00
p @ _ = > Err ( Box ::new ( Error ::UnsupportedFormat ( p . to_owned ( ) ) ) ) ,
}
}
2020-10-26 01:42:12 +00:00
#[ derive(Debug, Clone, Copy, Eq, PartialEq) ]
2020-10-25 21:39:38 +00:00
pub enum MimeType {
2020-10-25 16:29:14 +00:00
Png ,
Jpeg ,
Tiff ,
Bmp ,
Gif ,
}
2020-10-25 21:39:38 +00:00
impl From < MimeType > for String {
fn from ( mt : MimeType ) -> Self {
match mt {
MimeType ::Jpeg = > " image/jpeg " . to_owned ( ) ,
MimeType ::Png = > " image/png " . to_owned ( ) ,
MimeType ::Tiff = > " image/tiff " . to_owned ( ) ,
MimeType ::Bmp = > " image/bmp " . to_owned ( ) ,
MimeType ::Gif = > " image/gif " . to_owned ( ) ,
}
}
}
2020-10-26 01:42:12 +00:00
#[ derive(Debug, Clone, Eq, PartialEq) ]
2020-10-25 16:29:14 +00:00
pub struct Picture {
pub data : Vec < u8 > ,
2020-10-25 21:39:38 +00:00
pub mime_type : MimeType ,
2020-10-25 14:58:50 +00:00
}
impl Picture {
2020-10-25 16:29:14 +00:00
pub fn try_with_mime ( data : Vec < u8 > , mime : & str ) -> Result < Self , ( ) > {
2020-10-25 21:39:38 +00:00
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 ,
2020-10-25 16:29:14 +00:00
_ = > return Err ( ( ) ) ,
} ;
2020-10-25 21:39:38 +00:00
Ok ( Self { data , mime_type } )
2020-10-25 14:58:50 +00:00
}
}
#[ derive(Debug) ]
pub struct Album {
pub title : String ,
pub artist : Option < String > ,
pub cover : Option < Picture > ,
}
/// 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 ) ;
2020-10-25 21:39:38 +00:00
fn remove_title ( & mut self ) ;
2020-10-26 01:18:49 +00:00
2020-10-25 14:58:50 +00:00
fn artist ( & self ) -> Option < & str > ;
fn set_artist ( & mut self , artist : & str ) ;
2020-10-26 01:18:49 +00:00
fn remove_artist ( & mut self ) ;
2020-10-25 14:58:50 +00:00
fn year ( & self ) -> Option < i32 > ;
fn set_year ( & mut self , year : i32 ) ;
2020-10-25 21:39:38 +00:00
fn remove_year ( & mut self ) ;
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn album ( & self ) -> Option < Album > {
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 ( )
}
}
2020-10-26 01:18:49 +00:00
fn remove_album ( & mut self ) {
self . remove_album_title ( ) ;
self . remove_album_artist ( ) ;
self . remove_album_cover ( ) ;
}
fn album_title ( & self ) -> Option < & str > ;
2020-10-25 14:58:50 +00:00
fn set_album_title ( & mut self , v : & str ) ;
2020-10-26 01:18:49 +00:00
fn remove_album_title ( & mut self ) ;
fn album_artist ( & self ) -> Option < & str > ;
2020-10-25 14:58:50 +00:00
fn set_album_artist ( & mut self , v : & str ) ;
2020-10-26 01:18:49 +00:00
fn remove_album_artist ( & mut self ) ;
fn album_cover ( & self ) -> Option < Picture > ;
2020-10-25 14:58:50 +00:00
fn set_album_cover ( & mut self , cover : Picture ) ;
2020-10-26 01:18:49 +00:00
fn remove_album_cover ( & mut self ) ;
2020-10-25 21:39:38 +00:00
fn track ( & self ) -> ( Option < u16 > , Option < u16 > ) {
( 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 ( ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn track_number ( & self ) -> Option < u16 > ;
2020-10-25 16:29:14 +00:00
fn set_track_number ( & mut self , track_number : u16 ) ;
2020-10-25 21:39:38 +00:00
fn remove_track_number ( & mut self ) ;
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn total_tracks ( & self ) -> Option < u16 > ;
2020-10-25 14:58:50 +00:00
fn set_total_tracks ( & mut self , total_track : u16 ) ;
2020-10-25 21:39:38 +00:00
fn remove_total_tracks ( & mut self ) ;
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn disc ( & self ) -> ( Option < u16 > , Option < u16 > ) {
( 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 ( ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn disc_number ( & self ) -> Option < u16 > ;
2020-10-25 16:29:14 +00:00
fn set_disc_number ( & mut self , disc_number : u16 ) ;
2020-10-25 21:39:38 +00:00
fn remove_disc_number ( & mut self ) ;
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn total_discs ( & self ) -> Option < u16 > ;
2020-10-25 16:29:14 +00:00
fn set_total_discs ( & mut self , total_discs : u16 ) ;
2020-10-25 21:39:38 +00:00
fn remove_total_discs ( & mut self ) ;
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn write_to ( & mut self , file : & mut File ) -> Result < ( ) , BoxedError > ;
2020-10-25 14:58:50 +00:00
// cannot use impl AsRef<Path>
2020-10-25 21:39:38 +00:00
fn write_to_path ( & mut self , path : & str ) -> Result < ( ) , BoxedError > ;
2020-10-25 14:58:50 +00:00
}
pub struct Id3Tags {
inner : id3 ::Tag ,
}
impl Id3Tags {
2020-10-25 21:39:38 +00:00
pub fn read_from_path ( path : impl AsRef < Path > ) -> Result < Self , BoxedError > {
2020-10-25 14:58:50 +00:00
Ok ( Self {
inner : id3 ::Tag ::read_from_path ( path ) ? ,
} )
}
}
impl AudioTagsIo for Id3Tags {
fn title ( & self ) -> Option < & str > {
self . inner . title ( )
}
fn set_title ( & mut self , title : & str ) {
self . inner . set_title ( title )
}
2020-10-25 21:39:38 +00:00
fn remove_title ( & mut self ) {
self . inner . remove_title ( ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 14:58:50 +00:00
fn artist ( & self ) -> Option < & str > {
self . inner . artist ( )
}
fn set_artist ( & mut self , artist : & str ) {
2020-10-26 00:54:47 +00:00
self . inner . set_artist ( artist )
2020-10-25 14:58:50 +00:00
}
2020-10-25 21:39:38 +00:00
fn remove_artist ( & mut self ) {
self . inner . remove_artist ( ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 14:58:50 +00:00
fn year ( & self ) -> Option < i32 > {
self . inner . year ( )
}
fn set_year ( & mut self , year : i32 ) {
self . inner . set_year ( year )
}
2020-10-25 21:39:38 +00:00
fn remove_year ( & mut self ) {
2020-10-26 01:18:49 +00:00
self . inner . remove ( " TYER " )
2020-10-25 21:39:38 +00:00
// self.inner.remove_year(); // TODO
2020-10-25 14:58:50 +00:00
}
2020-10-26 01:18:49 +00:00
2020-10-25 14:58:50 +00:00
fn album_title ( & self ) -> Option < & str > {
self . inner . album ( )
}
2020-10-26 01:18:49 +00:00
fn set_album_title ( & mut self , v : & str ) {
self . inner . set_album ( v )
}
fn remove_album_title ( & mut self ) {
self . inner . remove_album ( ) ;
}
2020-10-25 14:58:50 +00:00
fn album_artist ( & self ) -> Option < & str > {
self . inner . album_artist ( )
}
2020-10-26 01:18:49 +00:00
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 ( ) ;
}
2020-10-25 14:58:50 +00:00
fn album_cover ( & self ) -> Option < Picture > {
2020-10-25 16:29:14 +00:00
if let Some ( Ok ( pic ) ) = self
. inner
2020-10-25 14:58:50 +00:00
. pictures ( )
. filter ( | & pic | matches! ( pic . picture_type , id3 ::frame ::PictureType ::CoverFront ) )
. next ( )
2020-10-25 16:29:14 +00:00
. map ( | pic | Picture ::try_with_mime ( pic . data . clone ( ) , & pic . mime_type ) )
{
Some ( pic )
} else {
None
}
2020-10-25 14:58:50 +00:00
}
fn set_album_cover ( & mut self , cover : Picture ) {
2020-10-25 21:39:38 +00:00
self . remove_album_cover ( ) ;
2020-10-26 01:18:49 +00:00
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 ,
2020-10-25 14:58:50 +00:00
} ) ;
}
2020-10-25 21:39:38 +00:00
fn remove_album_cover ( & mut self ) {
self . inner
. remove_picture_by_type ( id3 ::frame ::PictureType ::CoverFront ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn track_number ( & self ) -> Option < u16 > {
self . inner . track ( ) . map ( | x | x as u16 )
}
2020-10-25 16:29:14 +00:00
fn set_track_number ( & mut self , track : u16 ) {
2020-10-25 14:58:50 +00:00
self . inner . set_track ( track as u32 ) ;
}
2020-10-25 21:39:38 +00:00
fn remove_track_number ( & mut self ) {
self . inner . remove_track ( ) ;
}
2020-10-26 01:18:49 +00:00
fn total_tracks ( & self ) -> Option < u16 > {
self . inner . total_tracks ( ) . map ( | x | x as u16 )
}
2020-10-25 14:58:50 +00:00
fn set_total_tracks ( & mut self , total_track : u16 ) {
self . inner . set_total_tracks ( total_track as u32 ) ;
}
2020-10-25 21:39:38 +00:00
fn remove_total_tracks ( & mut self ) {
self . inner . remove_total_tracks ( ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn disc_number ( & self ) -> Option < u16 > {
self . inner . disc ( ) . map ( | x | x as u16 )
}
2020-10-25 16:29:14 +00:00
fn set_disc_number ( & mut self , disc_number : u16 ) {
self . inner . set_disc ( disc_number as u32 )
}
2020-10-25 21:39:38 +00:00
fn remove_disc_number ( & mut self ) {
self . inner . remove_disc ( ) ;
}
2020-10-26 01:18:49 +00:00
fn total_discs ( & self ) -> Option < u16 > {
self . inner . total_discs ( ) . map ( | x | x as u16 )
}
2020-10-25 16:29:14 +00:00
fn set_total_discs ( & mut self , total_discs : u16 ) {
self . inner . set_total_discs ( total_discs as u32 )
}
2020-10-25 21:39:38 +00:00
fn remove_total_discs ( & mut self ) {
self . inner . remove_total_discs ( ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn write_to ( & mut self , file : & mut File ) -> Result < ( ) , BoxedError > {
2020-10-25 14:58:50 +00:00
self . inner . write_to ( file , id3 ::Version ::Id3v24 ) ? ;
Ok ( ( ) )
}
2020-10-25 21:39:38 +00:00
fn write_to_path ( & mut self , path : & str ) -> Result < ( ) , BoxedError > {
2020-10-25 14:58:50 +00:00
self . inner . write_to_path ( path , id3 ::Version ::Id3v24 ) ? ;
Ok ( ( ) )
}
}
pub struct M4aTags {
inner : mp4ameta ::Tag ,
}
impl M4aTags {
2020-10-25 21:39:38 +00:00
pub fn read_from_path ( path : impl AsRef < Path > ) -> Result < Self , BoxedError > {
2020-10-25 14:58:50 +00:00
Ok ( Self {
inner : mp4ameta ::Tag ::read_from_path ( path ) ? ,
} )
}
}
impl AudioTagsIo for M4aTags {
fn title ( & self ) -> Option < & str > {
self . inner . title ( )
}
fn set_title ( & mut self , title : & str ) {
self . inner . set_title ( title )
}
2020-10-26 01:18:49 +00:00
2020-10-25 14:58:50 +00:00
fn artist ( & self ) -> Option < & str > {
self . inner . artist ( )
}
fn set_artist ( & mut self , artist : & str ) {
2020-10-26 01:42:12 +00:00
self . inner . set_artist ( artist )
2020-10-25 14:58:50 +00:00
}
2020-10-26 01:18:49 +00:00
2020-10-25 14:58:50 +00:00
fn year ( & self ) -> Option < i32 > {
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 ( ) )
}
2020-10-26 01:18:49 +00:00
fn album_title ( & self ) -> Option < & str > {
self . inner . album ( )
}
fn set_album_title ( & mut self , v : & str ) {
self . inner . set_album ( v )
}
fn album_artist ( & self ) -> Option < & str > {
self . inner . album_artist ( )
}
fn set_album_artist ( & mut self , v : & str ) {
self . inner . set_album_artist ( v )
}
2020-10-25 14:58:50 +00:00
fn album_cover ( & self ) -> Option < Picture > {
use mp4ameta ::Data ::* ;
2020-10-25 16:29:14 +00:00
if let Some ( Some ( pic ) ) = self . inner . artwork ( ) . map ( | data | match data {
Jpeg ( d ) = > Some ( Picture {
data : d . clone ( ) ,
2020-10-25 21:39:38 +00:00
mime_type : MimeType ::Jpeg ,
2020-10-25 16:29:14 +00:00
} ) ,
Png ( d ) = > Some ( Picture {
data : d . clone ( ) ,
2020-10-25 21:39:38 +00:00
mime_type : MimeType ::Png ,
2020-10-25 16:29:14 +00:00
} ) ,
_ = > None ,
} ) {
Some ( pic )
} else {
None
}
2020-10-25 14:58:50 +00:00
}
fn set_album_cover ( & mut self , cover : Picture ) {
2020-10-25 21:39:38 +00:00
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 ) ,
2020-10-25 14:58:50 +00:00
_ = > panic! ( " Only png and jpeg are supported in m4a " ) ,
} ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn track_number ( & self ) -> Option < u16 > {
self . inner . track_number ( )
}
fn total_tracks ( & self ) -> Option < u16 > {
self . inner . total_tracks ( )
2020-10-25 14:58:50 +00:00
}
2020-10-25 16:29:14 +00:00
fn set_track_number ( & mut self , track : u16 ) {
2020-10-25 14:58:50 +00:00
self . inner . set_track_number ( track ) ;
}
fn set_total_tracks ( & mut self , total_track : u16 ) {
self . inner . set_total_tracks ( total_track ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn disc_number ( & self ) -> Option < u16 > {
self . inner . disc_number ( )
}
fn total_discs ( & self ) -> Option < u16 > {
self . inner . total_discs ( )
2020-10-25 16:29:14 +00:00
}
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 )
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn remove_title ( & mut self ) {
self . inner . remove_title ( ) ;
}
fn remove_artist ( & mut self ) {
2020-10-26 01:42:12 +00:00
self . inner . remove_data ( mp4ameta ::atom ::ARTIST ) ;
2020-10-25 21:39:38 +00:00
// self.inner.remove_artist(); // TODO:
}
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 ) {
2020-10-26 01:42:12 +00:00
self . inner . remove_data ( mp4ameta ::atom ::ALBUM_ARTIST ) ;
2020-10-25 21:39:38 +00:00
// self.inner.remove_album_artist(); // TODO:
}
fn remove_album_cover ( & mut self ) {
self . inner . remove_artwork ( ) ;
}
fn remove_track ( & mut self ) {
self . inner . remove_track ( ) ;
}
fn remove_track_number ( & mut self ) {
2020-10-26 01:18:49 +00:00
// self.inner.remove_data(mp4ameta::atom::TRACK_NUMBER); not correct
2020-10-25 21:39:38 +00:00
// 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 ) {
2020-10-26 01:18:49 +00:00
// self.inner.remove_data(mp4ameta::atom::DISC_NUMBER); not correct
2020-10-25 21:39:38 +00:00
// TODO: self.inner.remove_disc_number();
}
fn remove_total_discs ( & mut self ) {
// TODO: self.inner.remove_total_discs();
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
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 ( ( ) )
}
}
struct FlacTags {
inner : metaflac ::Tag ,
}
impl FlacTags {
pub fn read_from_path ( path : impl AsRef < Path > ) -> Result < Self , BoxedError > {
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 FlacTags {
fn title ( & self ) -> Option < & str > {
self . get_first ( " TITLE " )
}
fn set_title ( & mut self , title : & str ) {
self . set_first ( " TITLE " , title ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn artist ( & self ) -> Option < & str > {
self . get_first ( " ARTIST " )
}
fn set_artist ( & mut self , artist : & str ) {
self . set_first ( " ARTIST " , artist )
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn year ( & self ) -> Option < i32 > {
if let Some ( Ok ( y ) ) = self
. get_first ( " DATE " )
. map ( | s | s . chars ( ) . take ( 4 ) . collect ::< String > ( ) . parse ::< i32 > ( ) )
{
Some ( y )
} else if let Some ( Ok ( y ) ) = self . get_first ( " YEAR " ) . map ( | s | s . parse ::< i32 > ( ) ) {
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 ( ) ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn album_title ( & self ) -> Option < & str > {
self . get_first ( " ALBUM " )
}
fn set_album_title ( & mut self , title : & str ) {
self . set_first ( " ALBUM " , title )
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn album_artist ( & self ) -> Option < & str > {
self . get_first ( " ALBUMARTIST " )
}
fn set_album_artist ( & mut self , v : & str ) {
self . set_first ( " ALBUMARTIST " , v )
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn album_cover ( & self ) -> Option < Picture > {
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 ) ;
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn track_number ( & self ) -> Option < u16 > {
if let Some ( Ok ( n ) ) = self . get_first ( " TRACKNUMBER " ) . map ( | x | x . parse ::< u16 > ( ) ) {
Some ( n )
} else {
None
}
}
fn set_track_number ( & mut self , v : u16 ) {
self . set_first ( " TRACKNUMBER " , & v . to_string ( ) )
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
// ! not standard
fn total_tracks ( & self ) -> Option < u16 > {
if let Some ( Ok ( n ) ) = self . get_first ( " TOTALTRACKS " ) . map ( | x | x . parse ::< u16 > ( ) ) {
Some ( n )
} else {
None
}
}
fn set_total_tracks ( & mut self , v : u16 ) {
self . set_first ( " TOTALTRACKS " , & v . to_string ( ) )
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
fn disc_number ( & self ) -> Option < u16 > {
if let Some ( Ok ( n ) ) = self . get_first ( " DISCNUMBER " ) . map ( | x | x . parse ::< u16 > ( ) ) {
Some ( n )
} else {
None
}
}
fn set_disc_number ( & mut self , v : u16 ) {
self . set_first ( " DISCNUMBER " , & v . to_string ( ) )
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
// ! not standard
fn total_discs ( & self ) -> Option < u16 > {
if let Some ( Ok ( n ) ) = self . get_first ( " TOTALDISCS " ) . map ( | x | x . parse ::< u16 > ( ) ) {
Some ( n )
} else {
None
}
}
fn set_total_discs ( & mut self , v : u16 ) {
self . set_first ( " TOTALDISCS " , & v . to_string ( ) )
}
2020-10-26 01:18:49 +00:00
2020-10-25 21:39:38 +00:00
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 > {
2020-10-25 14:58:50 +00:00
self . inner . write_to ( file ) ? ;
Ok ( ( ) )
}
2020-10-25 21:39:38 +00:00
fn write_to_path ( & mut self , path : & str ) -> Result < ( ) , BoxedError > {
2020-10-25 14:58:50 +00:00
self . inner . write_to_path ( path ) ? ;
Ok ( ( ) )
}
}