mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-13 14:12:31 +00:00
ID3v2: Introduce ExtendedUrlFrame
This commit is contained in:
parent
a126dfa196
commit
d0b9a50d03
7 changed files with 74 additions and 26 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::error::{ID3v2Error, ID3v2ErrorKind, LoftyError, Result};
|
||||
use crate::id3::v2::frame::FrameValue;
|
||||
use crate::id3::v2::items::{
|
||||
ExtendedTextFrame, LanguageFrame, Popularimeter, UniqueFileIdentifierFrame,
|
||||
ExtendedTextFrame, LanguageFrame, Popularimeter, UniqueFileIdentifierFrame, ExtendedUrlFrame
|
||||
};
|
||||
use crate::id3::v2::ID3v2Version;
|
||||
use crate::macros::err;
|
||||
|
@ -73,7 +73,7 @@ fn parse_user_defined(
|
|||
Ok(Some(if link {
|
||||
let content = decode_text(content, TextEncoding::Latin1, false)?.unwrap_or_default();
|
||||
|
||||
FrameValue::UserURL(ExtendedTextFrame {
|
||||
FrameValue::UserURL(ExtendedUrlFrame {
|
||||
encoding,
|
||||
description,
|
||||
content,
|
||||
|
|
|
@ -3,19 +3,19 @@ mod header;
|
|||
pub(super) mod id;
|
||||
pub(super) mod read;
|
||||
|
||||
use super::items::{ExtendedTextFrame, LanguageFrame, Popularimeter, UniqueFileIdentifierFrame};
|
||||
use super::items::{
|
||||
ExtendedTextFrame, ExtendedUrlFrame, LanguageFrame, Popularimeter, UniqueFileIdentifierFrame,
|
||||
};
|
||||
use super::util::upgrade::{upgrade_v2, upgrade_v3};
|
||||
use super::ID3v2Version;
|
||||
use crate::error::{ID3v2Error, ID3v2ErrorKind, LoftyError, Result};
|
||||
use crate::id3::v2::util::upgrade::{upgrade_v2, upgrade_v3};
|
||||
use crate::id3::v2::ID3v2Version;
|
||||
use crate::picture::Picture;
|
||||
use crate::tag::item::{ItemValue, TagItem};
|
||||
use crate::tag::item::{ItemKey, ItemValue, TagItem};
|
||||
use crate::tag::TagType;
|
||||
use crate::util::text::{encode_text, TextEncoding};
|
||||
use crate::ItemKey;
|
||||
use id::FrameID;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
|
@ -185,8 +185,8 @@ pub enum FrameValue {
|
|||
URL(String),
|
||||
/// Represents a "WXXX" frame
|
||||
///
|
||||
/// Due to the amount of information needed, it is contained in a separate struct, [`ExtendedTextFrame`]
|
||||
UserURL(ExtendedTextFrame),
|
||||
/// Due to the amount of information needed, it is contained in a separate struct, [`ExtendedUrlFrame`]
|
||||
UserURL(ExtendedUrlFrame),
|
||||
/// Represents an "APIC" or "PIC" frame
|
||||
Picture {
|
||||
/// The encoding of the description
|
||||
|
@ -232,7 +232,8 @@ impl FrameValue {
|
|||
content.insert(0, *encoding as u8);
|
||||
content
|
||||
},
|
||||
FrameValue::UserText(content) | FrameValue::UserURL(content) => content.as_bytes(),
|
||||
FrameValue::UserText(content) => content.as_bytes(),
|
||||
FrameValue::UserURL(content) => content.as_bytes(),
|
||||
FrameValue::URL(link) => link.as_bytes().to_vec(),
|
||||
FrameValue::Picture { encoding, picture } => {
|
||||
picture.as_apic_bytes(ID3v2Version::V4, *encoding)?
|
||||
|
@ -312,7 +313,7 @@ impl From<TagItem> for Option<Frame<'static>> {
|
|||
(FrameID::Valid(ref s), ItemValue::Locator(text) | ItemValue::Text(text))
|
||||
if s == "WXXX" =>
|
||||
{
|
||||
FrameValue::UserURL(ExtendedTextFrame {
|
||||
FrameValue::UserURL(ExtendedUrlFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: EMPTY_CONTENT_DESCRIPTOR,
|
||||
content: text,
|
||||
|
@ -345,7 +346,7 @@ impl From<TagItem> for Option<Frame<'static>> {
|
|||
},
|
||||
ItemValue::Locator(locator) => {
|
||||
frame_id = FrameID::Valid(Cow::Borrowed("WXXX"));
|
||||
value = FrameValue::UserURL(ExtendedTextFrame {
|
||||
value = FrameValue::UserURL(ExtendedUrlFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: String::from(desc),
|
||||
content: locator,
|
||||
|
@ -426,14 +427,14 @@ impl<'a> TryFrom<&'a TagItem> for FrameRef<'a> {
|
|||
content: text.clone(),
|
||||
}),
|
||||
("WXXX", ItemValue::Locator(text) | ItemValue::Text(text)) => {
|
||||
FrameValue::UserURL(ExtendedTextFrame {
|
||||
FrameValue::UserURL(ExtendedUrlFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: EMPTY_CONTENT_DESCRIPTOR,
|
||||
content: text.clone(),
|
||||
})
|
||||
},
|
||||
(locator_id, ItemValue::Locator(text)) if locator_id.len() > 4 => {
|
||||
FrameValue::UserURL(ExtendedTextFrame {
|
||||
FrameValue::UserURL(ExtendedUrlFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: EMPTY_CONTENT_DESCRIPTOR,
|
||||
content: text.clone(),
|
||||
|
@ -475,7 +476,7 @@ impl<'a> TryFrom<&'a TagItem> for FrameRef<'a> {
|
|||
},
|
||||
ItemValue::Locator(locator) => {
|
||||
frame_id = FrameID::Valid(Cow::Borrowed("WXXX"));
|
||||
value = FrameValue::UserURL(ExtendedTextFrame {
|
||||
value = FrameValue::UserURL(ExtendedUrlFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: String::from(desc),
|
||||
content: locator.clone(),
|
||||
|
|
|
@ -2,11 +2,11 @@ use crate::util::text::{encode_text, TextEncoding};
|
|||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// An `ID3v2` text frame
|
||||
/// An extended `ID3v2` text frame
|
||||
///
|
||||
/// This is used in the frames `TXXX` and `WXXX`, where the frames
|
||||
/// This is used in the `TXXX` frame, where the frames
|
||||
/// are told apart by descriptions, rather than their [`FrameID`](crate::id3::v2::FrameID)s.
|
||||
/// This means for each `EncodedTextFrame` in the tag, the description
|
||||
/// This means for each `ExtendedTextFrame` in the tag, the description
|
||||
/// must be unique.
|
||||
#[derive(Clone, Debug, Eq)]
|
||||
pub struct ExtendedTextFrame {
|
43
src/id3/v2/items/extended_url_frame.rs
Normal file
43
src/id3/v2/items/extended_url_frame.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use crate::util::text::{encode_text, TextEncoding};
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// An extended `ID3v2` URL frame
|
||||
///
|
||||
/// This is used in the `WXXX` frame, where the frames
|
||||
/// are told apart by descriptions, rather than their [`FrameID`](crate::id3::v2::FrameID)s.
|
||||
/// This means for each `ExtendedUrlFrame` in the tag, the description
|
||||
/// must be unique.
|
||||
#[derive(Clone, Debug, Eq)]
|
||||
pub struct ExtendedUrlFrame {
|
||||
/// The encoding of the description and comment text
|
||||
pub encoding: TextEncoding,
|
||||
/// Unique content description
|
||||
pub description: String,
|
||||
/// The actual frame content
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl PartialEq for ExtendedUrlFrame {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.description == other.description
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for ExtendedUrlFrame {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.description.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendedUrlFrame {
|
||||
/// Convert an [`ExtendedUrlFrame`] to a byte vec
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![self.encoding as u8];
|
||||
|
||||
bytes.extend(encode_text(&self.description, self.encoding, true).iter());
|
||||
bytes.extend(encode_text(&self.content, self.encoding, false));
|
||||
|
||||
bytes
|
||||
}
|
||||
}
|
|
@ -1,13 +1,15 @@
|
|||
mod encapsulated_object;
|
||||
mod encoded_text_frame;
|
||||
mod extended_text_frame;
|
||||
mod extended_url_frame;
|
||||
mod identifier;
|
||||
mod language_frame;
|
||||
mod popularimeter;
|
||||
mod sync_text;
|
||||
|
||||
pub use encapsulated_object::{GEOBInformation, GeneralEncapsulatedObject};
|
||||
pub use encoded_text_frame::ExtendedTextFrame;
|
||||
pub use extended_text_frame::ExtendedTextFrame;
|
||||
pub use identifier::UniqueFileIdentifierFrame;
|
||||
pub use extended_url_frame::ExtendedUrlFrame;
|
||||
pub use language_frame::LanguageFrame;
|
||||
pub use popularimeter::Popularimeter;
|
||||
pub use sync_text::{SyncTextContentType, SyncTextInformation, SynchronizedText, TimestampFormat};
|
||||
|
|
0
src/id3/v2/items/unsync_text.rs
Normal file
0
src/id3/v2/items/unsync_text.rs
Normal file
|
@ -4,7 +4,9 @@ use super::frame::{Frame, FrameFlags, FrameValue, EMPTY_CONTENT_DESCRIPTOR, UNKN
|
|||
use super::ID3v2Version;
|
||||
use crate::error::{LoftyError, Result};
|
||||
use crate::id3::v2::frame::{FrameRef, MUSICBRAINZ_UFID_OWNER};
|
||||
use crate::id3::v2::items::{ExtendedTextFrame, LanguageFrame, UniqueFileIdentifierFrame};
|
||||
use crate::id3::v2::items::{
|
||||
ExtendedTextFrame, ExtendedUrlFrame, LanguageFrame, UniqueFileIdentifierFrame,
|
||||
};
|
||||
use crate::picture::{Picture, PictureType, TOMBSTONE_PICTURE};
|
||||
use crate::tag::item::{ItemKey, ItemValue, TagItem};
|
||||
use crate::tag::{try_parse_year, Tag, TagType};
|
||||
|
@ -735,7 +737,7 @@ impl SplitTag for ID3v2Tag {
|
|||
},
|
||||
(
|
||||
"WXXX",
|
||||
FrameValue::UserURL(ExtendedTextFrame {
|
||||
FrameValue::UserURL(ExtendedUrlFrame {
|
||||
ref description,
|
||||
ref content,
|
||||
..
|
||||
|
@ -819,7 +821,7 @@ impl SplitTag for ID3v2Tag {
|
|||
return false; // Frame consumed
|
||||
},
|
||||
FrameValue::URL(content)
|
||||
| FrameValue::UserURL(ExtendedTextFrame { content, .. }) => {
|
||||
| FrameValue::UserURL(ExtendedUrlFrame { content, .. }) => {
|
||||
ItemValue::Locator(std::mem::take(content))
|
||||
},
|
||||
FrameValue::Picture { picture, .. } => {
|
||||
|
@ -1072,7 +1074,7 @@ mod tests {
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::id3::v2::frame::MUSICBRAINZ_UFID_OWNER;
|
||||
use crate::id3::v2::items::{Popularimeter, UniqueFileIdentifierFrame};
|
||||
use crate::id3::v2::items::{ExtendedUrlFrame, Popularimeter, UniqueFileIdentifierFrame};
|
||||
use crate::id3::v2::tag::{
|
||||
filter_comment_frame_by_description, new_text_frame, DEFAULT_NUMBER_IN_PAIR,
|
||||
};
|
||||
|
@ -1711,7 +1713,7 @@ mod tests {
|
|||
|
||||
let wxxx_frame = Frame::new(
|
||||
"WXXX",
|
||||
FrameValue::UserURL(ExtendedTextFrame {
|
||||
FrameValue::UserURL(ExtendedUrlFrame {
|
||||
encoding: TextEncoding::UTF8,
|
||||
description: String::from("BAR_URL_FRAME"),
|
||||
content: String::from("bar url"),
|
||||
|
|
Loading…
Reference in a new issue