2021-04-03 00:47:44 +00:00
|
|
|
|
//! [![Crate](https://img.shields.io/crates/v/lofty.svg)](https://crates.io/crates/lofty)
|
|
|
|
|
//! [![Crate](https://img.shields.io/crates/d/lofty.svg)](https://crates.io/crates/lofty)
|
|
|
|
|
//! [![Crate](https://img.shields.io/crates/l/lofty.svg)](https://crates.io/crates/lofty)
|
|
|
|
|
//! [![Documentation](https://docs.rs/lofty/badge.svg)](https://docs.rs/lofty/)
|
2020-10-27 02:08:05 +00:00
|
|
|
|
//!
|
2021-04-03 15:44:33 +00:00
|
|
|
|
//! Parse, convert, and write metadata to audio files of different file types.
|
2020-10-27 02:08:05 +00:00
|
|
|
|
//!
|
2020-10-27 14:38:31 +00:00
|
|
|
|
//! This crate aims to provide a unified trait for parsers and writers of different audio file formats.
|
2021-04-03 15:44:33 +00:00
|
|
|
|
//! Without this crate, you would otherwise need to learn the different APIs in **id3**, **mp4ameta**, etc.
|
|
|
|
|
//! in order to parse metadata in different file formats.
|
2020-10-27 02:08:05 +00:00
|
|
|
|
//!
|
2021-04-03 15:44:33 +00:00
|
|
|
|
//! # Supported Formats
|
|
|
|
|
//!
|
|
|
|
|
//! | File Format | Metadata Format | Backend |
|
|
|
|
|
//! |---------------|-----------------|-------------------------------------------------------------|
|
|
|
|
|
//! | `mp3` | ID3v2.4 | [**id3**](https://github.com/polyfloyd/rust-id3) |
|
|
|
|
|
//! | `wav` | TODO | TODO |
|
|
|
|
|
//! | `ape` | TODO | TODO |
|
|
|
|
|
//! | `opus` | Vorbis Comment | [**opus_headers**](https://github.com/zaethan/opus_headers) |
|
|
|
|
|
//! | `ogg` | Vorbis Comment | [**lewton**](https://github.com/RustAudio/lewton) |
|
|
|
|
|
//! | `flac` | Vorbis Comment | [**metaflac**](https://github.com/jameshurst/rust-metaflac) |
|
|
|
|
|
//! | `m4a/mp4/...` | Vorbis Comment | [**mp4ameta**](https://github.com/Saecki/rust-mp4ameta) |
|
|
|
|
|
//!
|
|
|
|
|
//! # Examples
|
|
|
|
|
//!
|
|
|
|
|
//! ```
|
|
|
|
|
//! use lofty::{Tag, TagType};
|
|
|
|
|
//!
|
|
|
|
|
//! // Guess the format from the extension, in this case `mp3`
|
|
|
|
|
//! let mut tag = Tag::new().read_from_path("assets/a.mp3").unwrap();
|
|
|
|
|
//! tag.set_title("Foo");
|
|
|
|
|
//!
|
|
|
|
|
//! // You can convert the tag type and save the metadata to another file.
|
|
|
|
|
//! tag.to_dyn_tag(TagType::Mp4).write_to_path("assets/a.m4a");
|
2020-10-27 02:08:05 +00:00
|
|
|
|
//!
|
2021-04-03 15:44:33 +00:00
|
|
|
|
//! // You can specify the tag type, but when you want to do this
|
|
|
|
|
//! // also consider directly using the concrete type
|
|
|
|
|
//! let tag = Tag::new().with_tag_type(TagType::Mp4).read_from_path("assets/a.m4a").unwrap();
|
|
|
|
|
//! assert_eq!(tag.title(), Some("Foo"));
|
|
|
|
|
//! ```
|
2020-10-27 02:08:05 +00:00
|
|
|
|
//!
|
2021-04-03 15:44:33 +00:00
|
|
|
|
//! ## Performance
|
|
|
|
|
//!
|
|
|
|
|
//! Using lofty incurs a little overhead due to vtables if you want to guess the metadata format (from file extension).
|
|
|
|
|
//! Apart from this, the performance is almost the same as directly calling the function provided from those ‘specialized’ crates.
|
2020-10-27 02:08:05 +00:00
|
|
|
|
//!
|
2021-04-03 15:44:33 +00:00
|
|
|
|
//! No copies will be made if you only need to read and write metadata of one format. If you want to convert between tags, copying is
|
|
|
|
|
//! unavoidable, no matter if you use lofty or use getters and setters provided by specialized libraries. Lofty is not making additional
|
|
|
|
|
//! unnecessary copies.
|
2020-10-25 14:58:50 +00:00
|
|
|
|
//!
|
2021-04-03 15:44:33 +00:00
|
|
|
|
//! Theoretically, it is possible to achieve zero-copy conversions if all parsers can parse into a unified struct.
|
|
|
|
|
//! However, this is going to be a lot of work.
|
2020-10-25 14:58:50 +00:00
|
|
|
|
|
2021-04-03 00:47:44 +00:00
|
|
|
|
//#![forbid(unused_crate_dependencies, unused_import_braces)]
|
|
|
|
|
#![warn(clippy::pedantic)]
|
|
|
|
|
#![allow(
|
|
|
|
|
clippy::too_many_lines,
|
|
|
|
|
clippy::cast_precision_loss,
|
|
|
|
|
clippy::cast_sign_loss,
|
|
|
|
|
clippy::cast_possible_wrap,
|
|
|
|
|
clippy::clippy::cast_possible_truncation,
|
|
|
|
|
clippy::module_name_repetitions
|
|
|
|
|
)]
|
|
|
|
|
|
2021-04-03 15:44:33 +00:00
|
|
|
|
#[doc(hidden)]
|
2021-04-03 01:07:28 +00:00
|
|
|
|
mod macros;
|
2020-10-27 11:40:51 +00:00
|
|
|
|
|
2021-04-03 15:44:33 +00:00
|
|
|
|
#[doc(hidden)]
|
2020-10-27 14:38:31 +00:00
|
|
|
|
pub mod anytag;
|
|
|
|
|
|
2021-04-03 15:44:33 +00:00
|
|
|
|
#[doc(hidden)]
|
2020-10-27 14:38:31 +00:00
|
|
|
|
pub mod components;
|
2020-10-26 02:14:28 +00:00
|
|
|
|
|
2021-04-03 15:44:33 +00:00
|
|
|
|
#[doc(hidden)]
|
2020-10-27 02:08:05 +00:00
|
|
|
|
pub mod error;
|
2020-10-26 23:16:04 +00:00
|
|
|
|
|
2020-10-27 14:38:31 +00:00
|
|
|
|
pub mod traits;
|
|
|
|
|
pub mod types;
|
2021-04-03 01:07:28 +00:00
|
|
|
|
|
2021-04-03 15:44:33 +00:00
|
|
|
|
#[doc(hidden)]
|
|
|
|
|
pub use {
|
|
|
|
|
anytag::*,
|
|
|
|
|
components::*,
|
|
|
|
|
macros::*,
|
|
|
|
|
std::convert::{TryFrom, TryInto},
|
|
|
|
|
traits::*,
|
|
|
|
|
types::*,
|
|
|
|
|
};
|
2020-10-27 11:40:51 +00:00
|
|
|
|
|
2021-04-03 15:44:33 +00:00
|
|
|
|
pub use error::TaggedError;
|
2020-10-25 14:58:50 +00:00
|
|
|
|
|
2021-04-03 15:44:33 +00:00
|
|
|
|
use std::{convert::From, fs::File, path::Path};
|
2020-10-26 15:37:10 +00:00
|
|
|
|
|
2020-10-29 18:01:21 +00:00
|
|
|
|
/// A builder for `Box<dyn AudioTag>`. If you do not want a trait object, you can use individual types.
|
2020-10-26 02:14:28 +00:00
|
|
|
|
#[derive(Default)]
|
2021-04-03 00:47:44 +00:00
|
|
|
|
pub struct Tag(Option<TagType>);
|
2020-10-26 02:14:28 +00:00
|
|
|
|
|
2020-10-26 20:43:11 +00:00
|
|
|
|
impl Tag {
|
2021-04-03 15:44:33 +00:00
|
|
|
|
/// Initiate a new Tag
|
2021-04-03 00:47:44 +00:00
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self::default()
|
|
|
|
|
}
|
2021-04-03 15:44:33 +00:00
|
|
|
|
/// This function can be used to specify a `TagType`, so there's no guessing
|
2021-04-03 00:47:44 +00:00
|
|
|
|
pub fn with_tag_type(self, tag_type: TagType) -> Self {
|
|
|
|
|
Self(Some(tag_type))
|
|
|
|
|
}
|
2021-04-03 01:43:32 +00:00
|
|
|
|
/// Path of the file to read
|
2021-04-03 00:47:44 +00:00
|
|
|
|
pub fn read_from_path(&self, path: impl AsRef<Path>) -> crate::Result<Box<dyn AudioTag>> {
|
|
|
|
|
let extension = path.as_ref().extension().unwrap().to_str().unwrap();
|
|
|
|
|
|
|
|
|
|
match self.0.unwrap_or(TagType::try_from_ext(extension)?) {
|
2021-04-03 01:43:32 +00:00
|
|
|
|
TagType::Id3v2 => Ok(Box::new(Id3v2Tag::read_from_path(path)?)),
|
|
|
|
|
TagType::Vorbis => Ok(Box::new(VorbisTag::read_from_path(path)?)),
|
|
|
|
|
TagType::Opus => Ok(Box::new(OpusTag::read_from_path(path)?)),
|
|
|
|
|
TagType::Flac => Ok(Box::new(FlacTag::read_from_path(path)?)),
|
|
|
|
|
TagType::Mp4 => Ok(Box::new(Mp4Tag::read_from_path(path)?)),
|
2021-04-03 00:47:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-26 20:43:11 +00:00
|
|
|
|
}
|
2020-10-29 18:01:21 +00:00
|
|
|
|
|
2021-04-03 15:44:33 +00:00
|
|
|
|
/// The type
|
2020-10-29 18:01:21 +00:00
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
|
pub enum TagType {
|
2021-04-03 15:44:33 +00:00
|
|
|
|
/// Common file extensions: `.mp3`
|
2021-04-03 00:47:44 +00:00
|
|
|
|
Id3v2,
|
2021-04-03 15:44:33 +00:00
|
|
|
|
/// Common file extensions: `.ogg`
|
2021-04-03 00:47:44 +00:00
|
|
|
|
Vorbis,
|
2021-04-03 15:44:33 +00:00
|
|
|
|
/// Common file extensions: `.opus`
|
2021-04-03 00:47:44 +00:00
|
|
|
|
Opus,
|
2021-04-03 15:44:33 +00:00
|
|
|
|
/// Common file extensions: `.flac`
|
2021-04-03 00:47:44 +00:00
|
|
|
|
Flac,
|
2021-04-03 15:44:33 +00:00
|
|
|
|
/// Common file extensions: `.mp4, .m4a, .m4p, .m4b, .m4r, .m4v`
|
2021-04-03 00:47:44 +00:00
|
|
|
|
Mp4,
|
2020-10-29 18:01:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TagType {
|
2021-04-03 00:47:44 +00:00
|
|
|
|
fn try_from_ext(ext: &str) -> crate::Result<Self> {
|
|
|
|
|
match ext {
|
|
|
|
|
"mp3" => Ok(Self::Id3v2),
|
|
|
|
|
"ogg" => Ok(Self::Vorbis),
|
|
|
|
|
"opus" => Ok(Self::Opus),
|
|
|
|
|
"flac" => Ok(Self::Flac),
|
|
|
|
|
"m4a" | "m4b" | "m4p" | "m4v" | "isom" | "mp4" => Ok(Self::Mp4),
|
|
|
|
|
_ => Err(TaggedError::UnsupportedFormat(ext.to_owned())),
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-29 18:01:21 +00:00
|
|
|
|
}
|