mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-11-10 14:44:22 +00:00
Start work on duration
Added a duration field to each tag struct, and implemented duration reading for mp3. All other tags return None for now.
This commit is contained in:
parent
0dd57cc4da
commit
8ff6bb52c8
9 changed files with 200 additions and 149 deletions
|
@ -1,13 +1,16 @@
|
|||
#![cfg(feature = "ape")]
|
||||
|
||||
use crate::{
|
||||
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Picture,
|
||||
Result, TagType, ToAny, ToAnyTag,
|
||||
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Picture, Result, TagType,
|
||||
ToAny, ToAnyTag,
|
||||
};
|
||||
|
||||
pub use ape::Tag as ApeInnerTag;
|
||||
use filepath::FilePath;
|
||||
use std::{fs::File, path::Path};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
#[cfg(feature = "duration")]
|
||||
use std::time::Duration;
|
||||
|
||||
impl_tag!(ApeTag, ApeInnerTag, TagType::Ape);
|
||||
|
||||
|
@ -17,7 +20,11 @@ impl ApeTag {
|
|||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Ok(Self(ape::read(path)?))
|
||||
Ok(Self {
|
||||
inner: ape::read(&path)?,
|
||||
#[cfg(feature = "duration")]
|
||||
duration: None, // TODO
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +45,8 @@ impl<'a> From<&'a ApeTag> for AnyTag<'a> {
|
|||
total_discs: inp.total_discs(),
|
||||
comments: None,
|
||||
date: None, // TODO
|
||||
duration_ms: None,
|
||||
#[cfg(feature = "duration")]
|
||||
duration: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +89,7 @@ impl<'a> From<AnyTag<'a>> for ApeTag {
|
|||
|
||||
impl ApeTag {
|
||||
fn get_value(&self, key: &str) -> Option<&str> {
|
||||
if let Some(item) = self.0.item(key) {
|
||||
if let Some(item) = self.inner.item(key) {
|
||||
if let ape::ItemValue::Text(val) = &item.value {
|
||||
return Some(&*val);
|
||||
}
|
||||
|
@ -99,11 +107,11 @@ impl ApeTag {
|
|||
value: ape::ItemValue::Text(val.into()),
|
||||
};
|
||||
|
||||
self.0.set_item(item)
|
||||
self.inner.set_item(item)
|
||||
}
|
||||
|
||||
fn remove_key(&mut self, key: &str) {
|
||||
let _ = self.0.remove_item(key);
|
||||
let _ = self.inner.remove_item(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,11 +292,11 @@ impl AudioTagEdit for ApeTag {
|
|||
impl AudioTagWrite for ApeTag {
|
||||
fn write_to(&self, file: &mut File) -> Result<()> {
|
||||
// Write only uses paths, this is annoying
|
||||
ape::write(&self.0, file.path()?)?;
|
||||
ape::write(&self.inner, file.path()?)?;
|
||||
Ok(())
|
||||
}
|
||||
fn write_to_path(&self, path: &str) -> Result<()> {
|
||||
ape::write(&self.0, path)?;
|
||||
ape::write(&self.inner, path)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
#![cfg(feature = "mp3")]
|
||||
|
||||
use crate::{
|
||||
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error,
|
||||
MimeType, Picture, Result, TagType, ToAny, ToAnyTag,
|
||||
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error, MimeType, Picture,
|
||||
Result, TagType, ToAny, ToAnyTag,
|
||||
};
|
||||
|
||||
pub use id3::Tag as Id3v2InnerTag;
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
#[cfg(feature = "duration")]
|
||||
use std::time::Duration;
|
||||
|
||||
impl_tag!(Id3v2Tag, Id3v2InnerTag, TagType::Id3v2);
|
||||
|
||||
|
@ -18,7 +20,11 @@ impl Id3v2Tag {
|
|||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Ok(Self(Id3v2InnerTag::read_from_path(path)?))
|
||||
Ok(Self {
|
||||
inner: Id3v2InnerTag::read_from_path(&path)?,
|
||||
#[cfg(feature = "duration")]
|
||||
duration: Some(mp3_duration::from_path(&path)?), // TODO
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +45,8 @@ impl<'a> From<&'a Id3v2Tag> for AnyTag<'a> {
|
|||
total_discs: inp.total_discs(),
|
||||
comments: None,
|
||||
date: None, // TODO
|
||||
duration_ms: None,
|
||||
#[cfg(feature = "duration")]
|
||||
duration: inp.duration,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,21 +105,21 @@ impl<'a> std::convert::TryFrom<&'a id3::frame::Picture> for Picture<'a> {
|
|||
|
||||
impl AudioTagEdit for Id3v2Tag {
|
||||
fn title(&self) -> Option<&str> {
|
||||
self.0.title()
|
||||
self.inner.title()
|
||||
}
|
||||
fn set_title(&mut self, title: &str) {
|
||||
self.0.set_title(title)
|
||||
self.inner.set_title(title)
|
||||
}
|
||||
fn remove_title(&mut self) {
|
||||
self.0.remove_title();
|
||||
self.inner.remove_title();
|
||||
}
|
||||
|
||||
fn artist_str(&self) -> Option<&str> {
|
||||
self.0.artist()
|
||||
self.inner.artist()
|
||||
}
|
||||
|
||||
fn set_artist(&mut self, artist: &str) {
|
||||
self.0.set_artist(artist)
|
||||
self.inner.set_artist(artist)
|
||||
}
|
||||
|
||||
fn artists_vec(&self) -> Option<Vec<&str>> {
|
||||
|
@ -120,47 +127,47 @@ impl AudioTagEdit for Id3v2Tag {
|
|||
}
|
||||
|
||||
fn remove_artist(&mut self) {
|
||||
self.0.remove_artist()
|
||||
self.inner.remove_artist()
|
||||
}
|
||||
|
||||
fn year(&self) -> Option<i32> {
|
||||
self.0.year()
|
||||
self.inner.year()
|
||||
}
|
||||
fn set_year(&mut self, year: i32) {
|
||||
self.0.set_year(year as i32)
|
||||
self.inner.set_year(year as i32)
|
||||
}
|
||||
fn remove_year(&mut self) {
|
||||
self.0.remove_year()
|
||||
self.inner.remove_year()
|
||||
}
|
||||
|
||||
fn album_title(&self) -> Option<&str> {
|
||||
self.0.album()
|
||||
self.inner.album()
|
||||
}
|
||||
fn set_album_title(&mut self, v: &str) {
|
||||
self.0.set_album(v)
|
||||
self.inner.set_album(v)
|
||||
}
|
||||
fn remove_album_title(&mut self) {
|
||||
self.0.remove_album();
|
||||
self.inner.remove_album();
|
||||
}
|
||||
|
||||
fn album_artist_str(&self) -> Option<&str> {
|
||||
self.0.album_artist()
|
||||
self.inner.album_artist()
|
||||
}
|
||||
|
||||
fn album_artists_vec(&self) -> Option<Vec<&str>> {
|
||||
self.0.album_artist().map(|a| a.split('/').collect())
|
||||
self.inner.album_artist().map(|a| a.split('/').collect())
|
||||
}
|
||||
|
||||
fn set_album_artist(&mut self, artists: &str) {
|
||||
self.0.set_album_artist(artists)
|
||||
self.inner.set_album_artist(artists)
|
||||
}
|
||||
|
||||
fn remove_album_artists(&mut self) {
|
||||
self.0.remove_album_artist()
|
||||
self.inner.remove_album_artist()
|
||||
}
|
||||
|
||||
fn album_cover(&self) -> Option<Picture> {
|
||||
self.0
|
||||
self.inner
|
||||
.pictures()
|
||||
.find(|&pic| matches!(pic.picture_type, id3::frame::PictureType::CoverFront))
|
||||
.and_then(|pic| {
|
||||
|
@ -172,7 +179,7 @@ impl AudioTagEdit for Id3v2Tag {
|
|||
}
|
||||
fn set_album_cover(&mut self, cover: Picture) {
|
||||
self.remove_album_cover();
|
||||
self.0.add_picture(id3::frame::Picture {
|
||||
self.inner.add_picture(id3::frame::Picture {
|
||||
mime_type: String::from(cover.mime_type),
|
||||
picture_type: id3::frame::PictureType::CoverFront,
|
||||
description: "".to_owned(),
|
||||
|
@ -180,58 +187,58 @@ impl AudioTagEdit for Id3v2Tag {
|
|||
});
|
||||
}
|
||||
fn remove_album_cover(&mut self) {
|
||||
self.0
|
||||
self.inner
|
||||
.remove_picture_by_type(id3::frame::PictureType::CoverFront);
|
||||
}
|
||||
|
||||
fn track_number(&self) -> Option<u32> {
|
||||
self.0.track()
|
||||
self.inner.track()
|
||||
}
|
||||
fn set_track_number(&mut self, track: u32) {
|
||||
self.0.set_track(track);
|
||||
self.inner.set_track(track);
|
||||
}
|
||||
fn remove_track_number(&mut self) {
|
||||
self.0.remove_track();
|
||||
self.inner.remove_track();
|
||||
}
|
||||
|
||||
fn total_tracks(&self) -> Option<u32> {
|
||||
self.0.total_tracks()
|
||||
self.inner.total_tracks()
|
||||
}
|
||||
fn set_total_tracks(&mut self, total_track: u32) {
|
||||
self.0.set_total_tracks(total_track as u32);
|
||||
self.inner.set_total_tracks(total_track as u32);
|
||||
}
|
||||
fn remove_total_tracks(&mut self) {
|
||||
self.0.remove_total_tracks();
|
||||
self.inner.remove_total_tracks();
|
||||
}
|
||||
|
||||
fn disc_number(&self) -> Option<u32> {
|
||||
self.0.disc()
|
||||
self.inner.disc()
|
||||
}
|
||||
fn set_disc_number(&mut self, disc_number: u32) {
|
||||
self.0.set_disc(disc_number as u32)
|
||||
self.inner.set_disc(disc_number as u32)
|
||||
}
|
||||
fn remove_disc_number(&mut self) {
|
||||
self.0.remove_disc();
|
||||
self.inner.remove_disc();
|
||||
}
|
||||
|
||||
fn total_discs(&self) -> Option<u32> {
|
||||
self.0.total_discs()
|
||||
self.inner.total_discs()
|
||||
}
|
||||
fn set_total_discs(&mut self, total_discs: u32) {
|
||||
self.0.set_total_discs(total_discs)
|
||||
self.inner.set_total_discs(total_discs)
|
||||
}
|
||||
fn remove_total_discs(&mut self) {
|
||||
self.0.remove_total_discs();
|
||||
self.inner.remove_total_discs();
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioTagWrite for Id3v2Tag {
|
||||
fn write_to(&self, file: &mut File) -> Result<()> {
|
||||
self.0.write_to(file, id3::Version::Id3v24)?;
|
||||
self.inner.write_to(file, id3::Version::Id3v24)?;
|
||||
Ok(())
|
||||
}
|
||||
fn write_to_path(&self, path: &str) -> Result<()> {
|
||||
self.0.write_to_path(path, id3::Version::Id3v24)?;
|
||||
self.inner.write_to_path(path, id3::Version::Id3v24)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
#![cfg(feature = "mp4")]
|
||||
|
||||
use crate::{
|
||||
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error,
|
||||
MimeType, Picture, Result, TagType, ToAny, ToAnyTag,
|
||||
impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Error, MimeType, Picture,
|
||||
Result, TagType, ToAny, ToAnyTag,
|
||||
};
|
||||
|
||||
pub use mp4ameta::{FourCC, Tag as Mp4InnerTag};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
#[cfg(feature = "duration")]
|
||||
use std::time::Duration;
|
||||
|
||||
impl_tag!(Mp4Tag, Mp4InnerTag, TagType::Mp4);
|
||||
|
||||
|
@ -17,7 +19,11 @@ impl Mp4Tag {
|
|||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Ok(Self(Mp4InnerTag::read_from_path(path)?))
|
||||
Ok(Self {
|
||||
inner: Mp4InnerTag::read_from_path(path)?,
|
||||
#[cfg(feature = "duration")]
|
||||
duration: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +51,8 @@ impl<'a> From<&'a Mp4Tag> for AnyTag<'a> {
|
|||
total_discs,
|
||||
comments: None,
|
||||
date: None,
|
||||
duration_ms: None, // TODO?
|
||||
#[cfg(feature = "duration")]
|
||||
duration: None, // TODO?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,62 +111,62 @@ impl<'a> std::convert::TryFrom<&'a mp4ameta::Data> for Picture<'a> {
|
|||
|
||||
impl AudioTagEdit for Mp4Tag {
|
||||
fn title(&self) -> Option<&str> {
|
||||
self.0.title()
|
||||
self.inner.title()
|
||||
}
|
||||
fn set_title(&mut self, title: &str) {
|
||||
self.0.set_title(title)
|
||||
self.inner.set_title(title)
|
||||
}
|
||||
|
||||
fn remove_title(&mut self) {
|
||||
self.0.remove_title();
|
||||
self.inner.remove_title();
|
||||
}
|
||||
fn artist_str(&self) -> Option<&str> {
|
||||
self.0.artist()
|
||||
self.inner.artist()
|
||||
}
|
||||
fn set_artist(&mut self, artist: &str) {
|
||||
self.0.set_artist(artist)
|
||||
self.inner.set_artist(artist)
|
||||
}
|
||||
|
||||
fn remove_artist(&mut self) {
|
||||
self.0.remove_artists();
|
||||
self.inner.remove_artists();
|
||||
}
|
||||
|
||||
fn year(&self) -> Option<i32> {
|
||||
self.0.year().and_then(|x| str::parse(x).ok())
|
||||
self.inner.year().and_then(|x| str::parse(x).ok())
|
||||
}
|
||||
fn set_year(&mut self, year: i32) {
|
||||
self.0.set_year(year.to_string())
|
||||
self.inner.set_year(year.to_string())
|
||||
}
|
||||
|
||||
fn remove_year(&mut self) {
|
||||
self.0.remove_year();
|
||||
self.inner.remove_year();
|
||||
}
|
||||
fn album_title(&self) -> Option<&str> {
|
||||
self.0.album()
|
||||
self.inner.album()
|
||||
}
|
||||
|
||||
fn set_album_title(&mut self, v: &str) {
|
||||
self.0.set_album(v)
|
||||
self.inner.set_album(v)
|
||||
}
|
||||
fn remove_album_title(&mut self) {
|
||||
self.0.remove_album();
|
||||
self.inner.remove_album();
|
||||
}
|
||||
|
||||
fn album_artist_str(&self) -> Option<&str> {
|
||||
self.0.album_artist()
|
||||
self.inner.album_artist()
|
||||
}
|
||||
|
||||
fn set_album_artist(&mut self, artists: &str) {
|
||||
self.0.set_album_artist(artists)
|
||||
self.inner.set_album_artist(artists)
|
||||
}
|
||||
|
||||
fn remove_album_artists(&mut self) {
|
||||
self.0.remove_album_artists();
|
||||
self.inner.remove_album_artists();
|
||||
}
|
||||
fn album_cover(&self) -> Option<Picture> {
|
||||
use mp4ameta::Data::{Jpeg, Png};
|
||||
|
||||
self.0.artwork().and_then(|data| match data {
|
||||
self.inner.artwork().and_then(|data| match data {
|
||||
Jpeg(d) => Some(Picture {
|
||||
data: d,
|
||||
mime_type: MimeType::Jpeg,
|
||||
|
@ -174,67 +181,67 @@ impl AudioTagEdit for Mp4Tag {
|
|||
|
||||
fn set_album_cover(&mut self, cover: Picture) {
|
||||
self.remove_album_cover();
|
||||
self.0.add_artwork(match cover.mime_type {
|
||||
self.inner.add_artwork(match cover.mime_type {
|
||||
MimeType::Png => mp4ameta::Data::Png(cover.data.to_owned()),
|
||||
MimeType::Jpeg => mp4ameta::Data::Jpeg(cover.data.to_owned()),
|
||||
_ => panic!("Only png and jpeg are supported in m4a"),
|
||||
});
|
||||
}
|
||||
fn remove_album_cover(&mut self) {
|
||||
self.0.remove_artwork();
|
||||
self.inner.remove_artwork();
|
||||
}
|
||||
fn remove_track(&mut self) {
|
||||
self.0.remove_track(); // faster than removing separately
|
||||
self.inner.remove_track(); // faster than removing separately
|
||||
}
|
||||
fn track_number(&self) -> Option<u32> {
|
||||
self.0.track_number().map(u32::from)
|
||||
self.inner.track_number().map(u32::from)
|
||||
}
|
||||
|
||||
fn set_track_number(&mut self, track: u32) {
|
||||
self.0.set_track_number(track as u16);
|
||||
self.inner.set_track_number(track as u16);
|
||||
}
|
||||
fn remove_track_number(&mut self) {
|
||||
self.0.remove_track_number();
|
||||
self.inner.remove_track_number();
|
||||
}
|
||||
fn total_tracks(&self) -> Option<u32> {
|
||||
self.0.total_tracks().map(u32::from)
|
||||
self.inner.total_tracks().map(u32::from)
|
||||
}
|
||||
fn set_total_tracks(&mut self, total_track: u32) {
|
||||
self.0.set_total_tracks(total_track as u16);
|
||||
self.inner.set_total_tracks(total_track as u16);
|
||||
}
|
||||
fn remove_total_tracks(&mut self) {
|
||||
self.0.remove_total_tracks();
|
||||
self.inner.remove_total_tracks();
|
||||
}
|
||||
fn remove_disc(&mut self) {
|
||||
self.0.remove_disc();
|
||||
self.inner.remove_disc();
|
||||
}
|
||||
fn disc_number(&self) -> Option<u32> {
|
||||
self.0.disc_number().map(u32::from)
|
||||
self.inner.disc_number().map(u32::from)
|
||||
}
|
||||
fn set_disc_number(&mut self, disc_number: u32) {
|
||||
self.0.set_disc_number(disc_number as u16)
|
||||
self.inner.set_disc_number(disc_number as u16)
|
||||
}
|
||||
fn remove_disc_number(&mut self) {
|
||||
self.0.remove_disc_number();
|
||||
self.inner.remove_disc_number();
|
||||
}
|
||||
fn total_discs(&self) -> Option<u32> {
|
||||
self.0.total_discs().map(u32::from)
|
||||
self.inner.total_discs().map(u32::from)
|
||||
}
|
||||
fn set_total_discs(&mut self, total_discs: u32) {
|
||||
self.0.set_total_discs(total_discs as u16)
|
||||
self.inner.set_total_discs(total_discs as u16)
|
||||
}
|
||||
fn remove_total_discs(&mut self) {
|
||||
self.0.remove_total_discs();
|
||||
self.inner.remove_total_discs();
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioTagWrite for Mp4Tag {
|
||||
fn write_to(&self, file: &mut File) -> Result<()> {
|
||||
self.0.write_to(&file)?;
|
||||
self.inner.write_to(&file)?;
|
||||
Ok(())
|
||||
}
|
||||
fn write_to_path(&self, path: &str) -> Result<()> {
|
||||
self.0.write_to_path(path)?;
|
||||
self.inner.write_to_path(path)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ use std::collections::HashMap;
|
|||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Cursor, Seek, SeekFrom, Write};
|
||||
use std::path::Path;
|
||||
#[cfg(feature = "duration")]
|
||||
use std::time::Duration;
|
||||
|
||||
const START_SIGNATURE: [u8; 7] = [3, 118, 111, 114, 98, 105, 115];
|
||||
const END_BYTE: u8 = 1;
|
||||
|
@ -118,7 +120,11 @@ impl VorbisTag {
|
|||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Ok(Self(VorbisInnerTag::from_path(path, format)?))
|
||||
Ok(Self {
|
||||
inner: VorbisInnerTag::from_path(path, format)?,
|
||||
#[cfg(feature = "duration")]
|
||||
duration: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,7 +210,7 @@ impl From<metaflac::Tag> for VorbisTag {
|
|||
(comments, vendor)
|
||||
};
|
||||
|
||||
tag.0 = VorbisInnerTag {
|
||||
tag.inner = VorbisInnerTag {
|
||||
format: Some(VorbisFormat::Flac),
|
||||
vendor,
|
||||
comments,
|
||||
|
@ -220,10 +226,10 @@ impl From<&VorbisTag> for metaflac::Tag {
|
|||
|
||||
tag.remove_blocks(metaflac::BlockType::VorbisComment);
|
||||
|
||||
let vendor = inp.0.vendor.clone();
|
||||
let vendor = inp.inner.vendor.clone();
|
||||
let mut comment_collection: HashMap<String, Vec<String>> = HashMap::new();
|
||||
|
||||
for (k, v) in inp.0.comments.clone() {
|
||||
for (k, v) in inp.inner.comments.clone() {
|
||||
comment_collection.insert(k, vec![v]);
|
||||
}
|
||||
|
||||
|
@ -240,80 +246,80 @@ impl From<&VorbisTag> for metaflac::Tag {
|
|||
|
||||
impl AudioTagEdit for VorbisTag {
|
||||
fn title(&self) -> Option<&str> {
|
||||
self.0.get_value("TITLE")
|
||||
self.inner.get_value("TITLE")
|
||||
}
|
||||
fn set_title(&mut self, title: &str) {
|
||||
self.0.set_value("TITLE", title);
|
||||
self.inner.set_value("TITLE", title);
|
||||
}
|
||||
|
||||
fn remove_title(&mut self) {
|
||||
self.0.remove_key("TITLE");
|
||||
self.inner.remove_key("TITLE");
|
||||
}
|
||||
fn artist_str(&self) -> Option<&str> {
|
||||
self.0.get_value("ARTIST")
|
||||
self.inner.get_value("ARTIST")
|
||||
}
|
||||
|
||||
fn set_artist(&mut self, artist: &str) {
|
||||
self.0.set_value("ARTIST", artist)
|
||||
self.inner.set_value("ARTIST", artist)
|
||||
}
|
||||
|
||||
fn remove_artist(&mut self) {
|
||||
self.0.remove_key("ARTIST");
|
||||
self.inner.remove_key("ARTIST");
|
||||
}
|
||||
|
||||
fn year(&self) -> Option<i32> {
|
||||
if let Some(Ok(y)) = self
|
||||
.0
|
||||
.inner
|
||||
.get_value("DATE")
|
||||
.map(|s| s.chars().take(4).collect::<String>().parse::<i32>())
|
||||
{
|
||||
Some(y)
|
||||
} else if let Some(Ok(y)) = self.0.get_value("YEAR").map(str::parse::<i32>) {
|
||||
} else if let Some(Ok(y)) = self.inner.get_value("YEAR").map(str::parse::<i32>) {
|
||||
Some(y)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn set_year(&mut self, year: i32) {
|
||||
self.0.set_value("DATE", &year.to_string());
|
||||
self.0.set_value("YEAR", &year.to_string());
|
||||
self.inner.set_value("DATE", &year.to_string());
|
||||
self.inner.set_value("YEAR", &year.to_string());
|
||||
}
|
||||
fn remove_year(&mut self) {
|
||||
self.0.remove_key("YEAR");
|
||||
self.0.remove_key("DATE");
|
||||
self.inner.remove_key("YEAR");
|
||||
self.inner.remove_key("DATE");
|
||||
}
|
||||
|
||||
fn album_title(&self) -> Option<&str> {
|
||||
self.0.get_value("ALBUM")
|
||||
self.inner.get_value("ALBUM")
|
||||
}
|
||||
fn set_album_title(&mut self, title: &str) {
|
||||
self.0.set_value("ALBUM", title)
|
||||
self.inner.set_value("ALBUM", title)
|
||||
}
|
||||
fn remove_album_title(&mut self) {
|
||||
self.0.remove_key("ALBUM");
|
||||
self.inner.remove_key("ALBUM");
|
||||
}
|
||||
|
||||
fn album_artist_str(&self) -> Option<&str> {
|
||||
self.0.get_value("ALBUMARTIST")
|
||||
self.inner.get_value("ALBUMARTIST")
|
||||
}
|
||||
|
||||
fn album_artists_vec(&self) -> Option<Vec<&str>> {
|
||||
self.0
|
||||
self.inner
|
||||
.get_value("ALBUMARTIST")
|
||||
.map(|a| a.split('/').collect())
|
||||
}
|
||||
|
||||
fn set_album_artist(&mut self, artist: &str) {
|
||||
self.0.set_value("ALBUMARTIST", artist)
|
||||
self.inner.set_value("ALBUMARTIST", artist)
|
||||
}
|
||||
|
||||
fn remove_album_artists(&mut self) {
|
||||
self.0.remove_key("ALBUMARTIST");
|
||||
self.inner.remove_key("ALBUMARTIST");
|
||||
}
|
||||
|
||||
fn album_cover(&self) -> Option<Picture> {
|
||||
// TODO
|
||||
// self.0
|
||||
// self.inner
|
||||
// .pictures()
|
||||
// .filter(|&pic| matches!(pic.picture_type, metaflac::block::PictureType::CoverFront))
|
||||
// .next()
|
||||
|
@ -330,84 +336,84 @@ impl AudioTagEdit for VorbisTag {
|
|||
// self.remove_album_cover();
|
||||
// let mime = String::from(cover.mime_type);
|
||||
// let picture_type = metaflac::block::PictureType::CoverFront;
|
||||
// self.0
|
||||
// self.inner
|
||||
// .add_picture(mime, picture_type, (cover.data).to_owned());
|
||||
}
|
||||
fn remove_album_cover(&mut self) {
|
||||
// TODO
|
||||
// self.0
|
||||
// self.inner
|
||||
// .remove_picture_type(metaflac::block::PictureType::CoverFront)
|
||||
}
|
||||
|
||||
fn track_number(&self) -> Option<u32> {
|
||||
if let Some(Ok(n)) = self.0.get_value("TRACKNUMBER").map(str::parse::<u32>) {
|
||||
if let Some(Ok(n)) = self.inner.get_value("TRACKNUMBER").map(str::parse::<u32>) {
|
||||
Some(n)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn set_track_number(&mut self, v: u32) {
|
||||
self.0.set_value("TRACKNUMBER", &v.to_string())
|
||||
self.inner.set_value("TRACKNUMBER", &v.to_string())
|
||||
}
|
||||
fn remove_track_number(&mut self) {
|
||||
self.0.remove_key("TRACKNUMBER");
|
||||
self.inner.remove_key("TRACKNUMBER");
|
||||
}
|
||||
|
||||
// ! not standard
|
||||
fn total_tracks(&self) -> Option<u32> {
|
||||
if let Some(Ok(n)) = self.0.get_value("TOTALTRACKS").map(str::parse::<u32>) {
|
||||
if let Some(Ok(n)) = self.inner.get_value("TOTALTRACKS").map(str::parse::<u32>) {
|
||||
Some(n)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn set_total_tracks(&mut self, v: u32) {
|
||||
self.0.set_value("TOTALTRACKS", &v.to_string())
|
||||
self.inner.set_value("TOTALTRACKS", &v.to_string())
|
||||
}
|
||||
fn remove_total_tracks(&mut self) {
|
||||
self.0.remove_key("TOTALTRACKS");
|
||||
self.inner.remove_key("TOTALTRACKS");
|
||||
}
|
||||
|
||||
fn disc_number(&self) -> Option<u32> {
|
||||
if let Some(Ok(n)) = self.0.get_value("DISCNUMBER").map(str::parse::<u32>) {
|
||||
if let Some(Ok(n)) = self.inner.get_value("DISCNUMBER").map(str::parse::<u32>) {
|
||||
Some(n)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn set_disc_number(&mut self, v: u32) {
|
||||
self.0.set_value("DISCNUMBER", &v.to_string())
|
||||
self.inner.set_value("DISCNUMBER", &v.to_string())
|
||||
}
|
||||
fn remove_disc_number(&mut self) {
|
||||
self.0.remove_key("DISCNUMBER");
|
||||
self.inner.remove_key("DISCNUMBER");
|
||||
}
|
||||
|
||||
// ! not standard
|
||||
fn total_discs(&self) -> Option<u32> {
|
||||
if let Some(Ok(n)) = self.0.get_value("TOTALDISCS").map(str::parse::<u32>) {
|
||||
if let Some(Ok(n)) = self.inner.get_value("TOTALDISCS").map(str::parse::<u32>) {
|
||||
Some(n)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn set_total_discs(&mut self, v: u32) {
|
||||
self.0.set_value("TOTALDISCS", &v.to_string())
|
||||
self.inner.set_value("TOTALDISCS", &v.to_string())
|
||||
}
|
||||
fn remove_total_discs(&mut self) {
|
||||
self.0.remove_key("TOTALDISCS");
|
||||
self.inner.remove_key("TOTALDISCS");
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioTagWrite for VorbisTag {
|
||||
fn write_to(&self, file: &mut File) -> Result<()> {
|
||||
if let Some(format) = self.0.format.clone() {
|
||||
if let Some(format) = self.inner.format.clone() {
|
||||
match format {
|
||||
VorbisFormat::Ogg => {
|
||||
let vendor = self.0.vendor.clone();
|
||||
let vendor = self.inner.vendor.clone();
|
||||
let vendor_bytes = vendor.as_bytes();
|
||||
|
||||
let comments: Vec<(String, String)> = self
|
||||
.0
|
||||
.inner
|
||||
.comments
|
||||
.iter()
|
||||
.map(|(a, b)| (a.to_string(), b.to_string()))
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use crate::{
|
||||
components::logic, impl_tag, Album, AnyTag, AudioTag, AudioTagEdit,
|
||||
AudioTagWrite, Picture, Result, TagType, ToAny, ToAnyTag,
|
||||
components::logic, impl_tag, Album, AnyTag, AudioTag, AudioTagEdit, AudioTagWrite, Picture,
|
||||
Result, TagType, ToAny, ToAnyTag,
|
||||
};
|
||||
|
||||
use std::borrow::BorrowMut;
|
||||
use std::fs::OpenOptions;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Cursor, Seek, SeekFrom, Write};
|
||||
use std::{collections::HashMap, fs::File, path::Path};
|
||||
use std::path::Path;
|
||||
#[cfg(feature = "duration")]
|
||||
use std::time::Duration;
|
||||
|
||||
struct WavInnerTag {
|
||||
data: Option<HashMap<String, String>>,
|
||||
|
@ -23,12 +26,16 @@ impl Default for WavInnerTag {
|
|||
impl WavTag {
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
pub fn read_from_path<P>(path: P) -> Result<Self>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Ok(Self(WavInnerTag {
|
||||
data: logic::read::wav(File::open(path)?)?
|
||||
}))
|
||||
Ok(Self {
|
||||
inner: WavInnerTag {
|
||||
data: logic::read::wav(File::open(path)?)?,
|
||||
},
|
||||
#[cfg(feature = "duration")]
|
||||
duration: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +99,7 @@ impl<'a> From<&'a WavTag> for AnyTag<'a> {
|
|||
|
||||
impl WavTag {
|
||||
fn get_value(&self, key: &str) -> Option<&str> {
|
||||
self.0
|
||||
self.inner
|
||||
.data
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
|
@ -110,15 +117,15 @@ impl WavTag {
|
|||
where
|
||||
V: Into<String>,
|
||||
{
|
||||
let mut data = self.0.data.clone().unwrap();
|
||||
let mut data = self.inner.data.clone().unwrap();
|
||||
let _ = data.insert(key.to_string(), val.into());
|
||||
self.0.data = Some(data);
|
||||
self.inner.data = Some(data);
|
||||
}
|
||||
|
||||
fn remove_key(&mut self, key: &str) {
|
||||
let mut data = self.0.data.clone().unwrap();
|
||||
let mut data = self.inner.data.clone().unwrap();
|
||||
data.retain(|k, _| k != key);
|
||||
self.0.data = Some(data);
|
||||
self.inner.data = Some(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,7 +273,7 @@ impl AudioTagEdit for WavTag {
|
|||
|
||||
impl AudioTagWrite for WavTag {
|
||||
fn write_to(&self, file: &mut File) -> Result<()> {
|
||||
if let Some(data) = self.0.data.clone() {
|
||||
if let Some(data) = self.inner.data.clone() {
|
||||
let mut chunk = Vec::new();
|
||||
|
||||
chunk.extend(riff::LIST_ID.value.iter());
|
||||
|
|
|
@ -17,6 +17,9 @@ pub enum Error {
|
|||
FlacTag(#[from] metaflac::Error),
|
||||
#[error(transparent)]
|
||||
Id3Tag(#[from] id3::Error),
|
||||
#[cfg(feature = "duration")]
|
||||
#[error(transparent)]
|
||||
MP3Duration(#[from] mp3_duration::MP3DurationError),
|
||||
#[error(transparent)]
|
||||
Mp4Tag(#[from] mp4ameta::Error),
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -3,11 +3,19 @@
|
|||
macro_rules! impl_tag {
|
||||
($tag:ident, $inner:ident, $tag_type:expr) => {
|
||||
#[doc(hidden)]
|
||||
pub struct $tag($inner);
|
||||
pub struct $tag {
|
||||
inner: $inner,
|
||||
#[cfg(feature = "duration")]
|
||||
duration: Option<Duration>,
|
||||
}
|
||||
|
||||
impl Default for $tag {
|
||||
fn default() -> Self {
|
||||
Self($inner::default())
|
||||
Self {
|
||||
inner: $inner::default(),
|
||||
#[cfg(feature = "duration")]
|
||||
duration: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,14 +47,18 @@ macro_rules! impl_tag {
|
|||
// From wrapper to inner (same type)
|
||||
impl From<$tag> for $inner {
|
||||
fn from(inp: $tag) -> Self {
|
||||
inp.0
|
||||
inp.inner
|
||||
}
|
||||
}
|
||||
|
||||
// From inner to wrapper (same type)
|
||||
impl From<$inner> for $tag {
|
||||
fn from(inp: $inner) -> Self {
|
||||
Self(inp)
|
||||
Self {
|
||||
inner: inp,
|
||||
#[cfg(feature = "duration")]
|
||||
duration: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,6 @@ pub trait AudioTagEdit {
|
|||
fn total_discs(&self) -> Option<u32>;
|
||||
fn set_total_discs(&mut self, total_discs: u32);
|
||||
fn remove_total_discs(&mut self);
|
||||
|
||||
}
|
||||
|
||||
pub trait AudioTagWrite {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::Album;
|
||||
#[cfg(feature = "duration")]
|
||||
use std::time::Duration;
|
||||
|
||||
/// The tag returned from `read_from_path`
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -14,7 +16,7 @@ pub struct AnyTag<'a> {
|
|||
pub disc_number: Option<u32>,
|
||||
pub total_discs: Option<u32>,
|
||||
#[cfg(feature = "duration")]
|
||||
pub duration_ms: Option<u32>,
|
||||
pub duration: Option<Duration>,
|
||||
}
|
||||
|
||||
impl<'a> AnyTag<'a> {
|
||||
|
@ -75,8 +77,8 @@ impl<'a> AnyTag<'a> {
|
|||
}
|
||||
#[cfg(feature = "duration")]
|
||||
/// Returns `duration`
|
||||
pub fn duration(&self) -> Option<u32> {
|
||||
self.duration_ms
|
||||
pub fn duration(&self) -> Option<Duration> {
|
||||
self.duration
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue