mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-11-10 06:34:18 +00:00
Add tag_writer
example
This commit is contained in:
parent
2273e4608f
commit
ea0f3007e8
4 changed files with 149 additions and 8 deletions
|
@ -32,6 +32,7 @@ riff_info_list = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3.5", features = ["html_reports"] }
|
criterion = { version = "0.3.5", features = ["html_reports"] }
|
||||||
|
structopt = { version = "0.3.25", default-features = false }
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
|
|
|
@ -12,7 +12,7 @@ fn main() {
|
||||||
let tags = tagged_file.tags();
|
let tags = tagged_file.tags();
|
||||||
|
|
||||||
if tags.is_empty() {
|
if tags.is_empty() {
|
||||||
println!("No tags found, exiting.");
|
eprintln!("No tags found, exiting.");
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,14 +45,14 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
input.clear();
|
input.clear();
|
||||||
println!("Bad input")
|
eprintln!("ERROR: Unexpected input")
|
||||||
}
|
}
|
||||||
|
|
||||||
let tag_remove = available_tag_types[to_remove.unwrap()];
|
let tag_remove = available_tag_types[to_remove.unwrap()];
|
||||||
|
|
||||||
if tag_remove.remove_from_path(path) {
|
if tag_remove.remove_from_path(path) {
|
||||||
println!("Removed tag: {:?}", tag_remove);
|
println!("INFO: Removed tag: `{:?}`", tag_remove);
|
||||||
} else {
|
} else {
|
||||||
println!("Failed to remove the tag")
|
eprintln!("ERROR: Failed to remove the tag")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
83
examples/tag_writer.rs
Normal file
83
examples/tag_writer.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use lofty::{Accessor, Probe, Tag};
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
#[structopt(name = "tag_writer", about = "A simple tag writer example")]
|
||||||
|
struct Opt {
|
||||||
|
#[structopt(short, long)]
|
||||||
|
title: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(short, long)]
|
||||||
|
artist: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(short = "A", long)]
|
||||||
|
album: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(short, long)]
|
||||||
|
genre: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(short, long)]
|
||||||
|
path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
|
let mut tagged_file = Probe::open(opt.path.as_str())
|
||||||
|
.expect("Error: Bad path provided!")
|
||||||
|
.read()
|
||||||
|
.expect("Error: Failed to read file!");
|
||||||
|
|
||||||
|
let tag = match tagged_file.primary_tag_mut() {
|
||||||
|
Some(primary_tag) => primary_tag,
|
||||||
|
None => {
|
||||||
|
if let Some(first_tag) = tagged_file.first_tag_mut() {
|
||||||
|
first_tag
|
||||||
|
} else {
|
||||||
|
let tag_type = tagged_file.primary_tag_type();
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"WARN: No tags found, creating a new tag of type `{:?}`",
|
||||||
|
tag_type
|
||||||
|
);
|
||||||
|
tagged_file.insert_tag(Tag::new(tag_type));
|
||||||
|
|
||||||
|
tagged_file.primary_tag_mut().unwrap()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Opt {
|
||||||
|
title: None,
|
||||||
|
artist: None,
|
||||||
|
album: None,
|
||||||
|
genre: None,
|
||||||
|
..
|
||||||
|
} = opt
|
||||||
|
{
|
||||||
|
eprintln!("ERROR: No options provided!");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(title) = opt.title {
|
||||||
|
tag.set_title(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(artist) = opt.artist {
|
||||||
|
tag.set_artist(artist)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(album) = opt.album {
|
||||||
|
tag.set_album(album)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(genre) = opt.genre {
|
||||||
|
tag.set_genre(genre)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.save_to_path(opt.path)
|
||||||
|
.expect("ERROR: Failed to write the tag!");
|
||||||
|
|
||||||
|
println!("INFO: Tag successfully updated!");
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ use crate::error::{LoftyError, Result};
|
||||||
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
@ -53,18 +54,21 @@ impl TaggedFile {
|
||||||
/// | `FLAC`, `Opus`, `Vorbis` | `VorbisComments` |
|
/// | `FLAC`, `Opus`, `Vorbis` | `VorbisComments` |
|
||||||
/// | `MP4` | `Mp4Ilst` |
|
/// | `MP4` | `Mp4Ilst` |
|
||||||
pub fn primary_tag(&self) -> Option<&Tag> {
|
pub fn primary_tag(&self) -> Option<&Tag> {
|
||||||
self.tag(&Self::primary_tag_type(self.ty))
|
self.tag(&self.primary_tag_type())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the file's "Primary tag"
|
/// Gets a mutable reference to the file's "Primary tag"
|
||||||
///
|
///
|
||||||
/// See [`primary_tag`](Self::primary_tag) for an explanation
|
/// See [`primary_tag`](Self::primary_tag) for an explanation
|
||||||
pub fn primary_tag_mut(&mut self) -> Option<&mut Tag> {
|
pub fn primary_tag_mut(&mut self) -> Option<&mut Tag> {
|
||||||
self.tag_mut(&Self::primary_tag_type(self.ty))
|
self.tag_mut(&self.primary_tag_type())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn primary_tag_type(f_ty: FileType) -> TagType {
|
/// Returns the file type's "primary" [`TagType`]
|
||||||
match f_ty {
|
///
|
||||||
|
/// See [`primary_tag`](Self::primary_tag) for an explanation
|
||||||
|
pub fn primary_tag_type(&self) -> TagType {
|
||||||
|
match self.ty {
|
||||||
#[cfg(feature = "id3v2")]
|
#[cfg(feature = "id3v2")]
|
||||||
FileType::AIFF | FileType::MP3 | FileType::WAV => TagType::Id3v2,
|
FileType::AIFF | FileType::MP3 | FileType::WAV => TagType::Id3v2,
|
||||||
#[cfg(feature = "ape")]
|
#[cfg(feature = "ape")]
|
||||||
|
@ -76,6 +80,11 @@ impl TaggedFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines whether the file supports the given [`TagType`]
|
||||||
|
pub fn supports_tag_type(&self, tag_type: TagType) -> bool {
|
||||||
|
self.ty.supports_tag_type(&tag_type)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns all tags
|
/// Returns all tags
|
||||||
pub fn tags(&self) -> &[Tag] {
|
pub fn tags(&self) -> &[Tag] {
|
||||||
self.tags.as_slice()
|
self.tags.as_slice()
|
||||||
|
@ -101,6 +110,32 @@ impl TaggedFile {
|
||||||
self.tags.iter_mut().find(|i| i.tag_type() == tag_type)
|
self.tags.iter_mut().find(|i| i.tag_type() == tag_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts a [`Tag`]
|
||||||
|
///
|
||||||
|
/// If a tag is replaced, it will be returned
|
||||||
|
pub fn insert_tag(&mut self, tag: Tag) -> Option<Tag> {
|
||||||
|
let tag_type = *tag.tag_type();
|
||||||
|
|
||||||
|
if self.supports_tag_type(tag_type) {
|
||||||
|
let ret = self.remove_tag(tag_type);
|
||||||
|
self.tags.push(tag);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a specific [`TagType`]
|
||||||
|
///
|
||||||
|
/// This will return the tag if it is removed
|
||||||
|
pub fn remove_tag(&mut self, tag_type: TagType) -> Option<Tag> {
|
||||||
|
self.tags
|
||||||
|
.iter()
|
||||||
|
.position(|t| t.tag_type() == &tag_type)
|
||||||
|
.map(|pos| self.tags.remove(pos))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the file's [`FileType`]
|
/// Returns the file's [`FileType`]
|
||||||
pub fn file_type(&self) -> &FileType {
|
pub fn file_type(&self) -> &FileType {
|
||||||
&self.ty
|
&self.ty
|
||||||
|
@ -110,6 +145,28 @@ impl TaggedFile {
|
||||||
pub fn properties(&self) -> &FileProperties {
|
pub fn properties(&self) -> &FileProperties {
|
||||||
&self.properties
|
&self.properties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to write all tags to a path
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// See [TaggedFile::save_to]
|
||||||
|
pub fn save_to_path(&self, path: impl AsRef<Path>) -> Result<()> {
|
||||||
|
self.save_to(&mut OpenOptions::new().read(true).write(true).open(path)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to write all tags to a file
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// See [`Tag::save_to`], however this is applicable to every tag in the `TaggedFile`.
|
||||||
|
pub fn save_to(&self, file: &mut File) -> Result<()> {
|
||||||
|
for tag in &self.tags {
|
||||||
|
tag.save_to(file)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||||
|
|
Loading…
Reference in a new issue