diff --git a/id3/id3v2tag.cpp b/id3/id3v2tag.cpp index f42ff23..35cee43 100644 --- a/id3/id3v2tag.cpp +++ b/id3/id3v2tag.cpp @@ -20,6 +20,43 @@ namespace TagParser { * \brief Implementation of TagParser::Tag for ID3v2 tags. */ +/*! + * \brief Allows multiple values for some fields. + * \remarks The standard defines no general rule applicable to all fields. + */ +bool Id3v2Tag::supportsMultipleValues(KnownField field) const +{ + switch (field) { + case KnownField::Album: + case KnownField::Artist: + case KnownField::Year: + case KnownField::RecordDate: + case KnownField::Title: + case KnownField::Genre: + case KnownField::TrackPosition: + case KnownField::DiskPosition: + case KnownField::Encoder: + case KnownField::Bpm: + case KnownField::Lyricist: + case KnownField::Length: + case KnownField::Language: + case KnownField::EncoderSettings: + case KnownField::Grouping: + case KnownField::RecordLabel: + case KnownField::Composer: + return m_majorVersion > 3; + case KnownField::Rating: + case KnownField::Comment: + case KnownField::Cover: + case KnownField::Lyrics: + case KnownField::SynchronizedLyrics: + return true; + default: + return false; + ; + } +} + /*! * \brief Works like the default implementation but adds additional values as well. */ diff --git a/id3/id3v2tag.h b/id3/id3v2tag.h index 4504d3a..1295ab4 100644 --- a/id3/id3v2tag.h +++ b/id3/id3v2tag.h @@ -71,6 +71,7 @@ public: bool canEncodingBeUsed(TagTextEncoding encoding) const override; bool supportsDescription(KnownField field) const override; bool supportsMimeType(KnownField field) const override; + bool supportsMultipleValues(KnownField field) const override; void parse(std::istream &sourceStream, const uint64 maximalSize, Diagnostics &diag); Id3v2TagMaker prepareMaking(Diagnostics &diag); @@ -130,7 +131,14 @@ inline bool Id3v2Tag::canEncodingBeUsed(TagTextEncoding encoding) const inline bool Id3v2Tag::supportsDescription(KnownField field) const { - return field == KnownField::Cover; + switch (field) { + case KnownField::Cover: + case KnownField::Lyrics: + case KnownField::SynchronizedLyrics: + return true; + default: + return false; + } } inline bool Id3v2Tag::supportsMimeType(KnownField field) const diff --git a/matroska/matroskatag.h b/matroska/matroskatag.h index b492f59..9ae3df3 100644 --- a/matroska/matroskatag.h +++ b/matroska/matroskatag.h @@ -64,9 +64,10 @@ public: static constexpr TagType tagType = TagType::MatroskaTag; static constexpr const char *tagName = "Matroska tag"; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8; - bool canEncodingBeUsed(TagTextEncoding encoding) const; - bool supportsTarget() const; - TagTargetLevel targetLevel() const; + bool canEncodingBeUsed(TagTextEncoding encoding) const override; + bool supportsTarget() const override; + bool supportsMultipleValues(KnownField field) const override; + TagTargetLevel targetLevel() const override; void parse(EbmlElement &tagElement, Diagnostics &diag); MatroskaTagMaker prepareMaking(Diagnostics &diag); @@ -92,6 +93,16 @@ inline bool MatroskaTag::supportsTarget() const return true; } +/*! + * \brief Allows multiple values for all fields. + * \remarks "Multiple items should never be stored as a list in a single TagString. If there is + * more than one tag of a certain type to be stored, then more than one SimpleTag should be used." + */ +inline bool MatroskaTag::supportsMultipleValues(KnownField) const +{ + return true; +} + inline TagTargetLevel MatroskaTag::targetLevel() const { return matroskaTagTargetLevel(m_target.level()); diff --git a/mp4/mp4tag.h b/mp4/mp4tag.h index 8b75633..eb2cf24 100644 --- a/mp4/mp4tag.h +++ b/mp4/mp4tag.h @@ -120,6 +120,7 @@ public: bool setValue(const char *mean, const char *name, const TagValue &value); using FieldMapBasedTag::hasField; bool hasField(KnownField value) const override; + bool supportsMultipleValues(KnownField) const override; void parse(Mp4Atom &metaAtom, Diagnostics &diag); Mp4TagMaker prepareMaking(Diagnostics &diag); @@ -163,6 +164,16 @@ inline bool Mp4Tag::setValue(const std::string &mean, const std::string &name, c return setValue(mean.data(), name.data(), value); } +/*! + * \brief Returns false for all fields (for now). + * \remarks Not sure whether iTunes-style MP4 tags allow this. Let's return false for now. + * \todo Do some research whether it is supported or not. + */ +inline bool Mp4Tag::supportsMultipleValues(KnownField) const +{ + return false; +} + } // namespace TagParser #endif // TAG_PARSER_MP4TAG_H diff --git a/tag.cpp b/tag.cpp index 39edff2..1fce5e2 100644 --- a/tag.cpp +++ b/tag.cpp @@ -288,6 +288,26 @@ unsigned int Tag::insertValues(const Tag &from, bool overwrite) * when subclassing. */ +/*! + * \fn Tag::supportsMultipleValues() + * \brief Returns an indications whether the specified field supports multiple values. + * \remarks + * - If you assign multiple values to a field which doesn't support multiple values, + * the tag implementation might just ignore additional values. It might also try + * to preserve the values nevertheless by bending the rules of the tag format + * specification when it is safe to do so. (Usually it is safe because additional + * values would be simply ignored by other applications.) + * - So it is not really mandatory to check this before adding multiple values. Nothing + * bad will happen otherwise. However, a GUI application could use this method to + * determine which widget to use. + * - In case it is not known whether multiple values are supported, this method returns + * false. If you know better, you can try to assign multiple values anyways. + * - If this method returns true, there might be further constraints, though. Eg. only + * one cover of a certain type may be present at a time in an ID3v2 tag. + * - The default implementation returns false for all fields. This might be overwritten + * when subclassing. + */ + /*! * \fn Tag::supportsField() * \brief Returns an indication whether the specified \a field diff --git a/tag.h b/tag.h index d2a0fee..4e9e668 100644 --- a/tag.h +++ b/tag.h @@ -121,6 +121,7 @@ public: virtual TagDataType proposedDataType(KnownField field) const; virtual bool supportsDescription(KnownField field) const; virtual bool supportsMimeType(KnownField field) const; + virtual bool supportsMultipleValues(KnownField field) const; virtual unsigned int insertValues(const Tag &from, bool overwrite); virtual void ensureTextValuesAreProperlyEncoded() = 0; @@ -230,6 +231,11 @@ inline bool Tag::supportsMimeType(KnownField) const return false; } +inline bool Tag::supportsMultipleValues(KnownField) const +{ + return false; +} + } // namespace TagParser #endif // TAG_PARSER_TAG_H diff --git a/vorbis/vorbiscomment.h b/vorbis/vorbiscomment.h index fdac148..4636951 100644 --- a/vorbis/vorbiscomment.h +++ b/vorbis/vorbiscomment.h @@ -44,6 +44,7 @@ public: const TagValue &vendor() const; void setVendor(const TagValue &vendor); + bool supportsMultipleValues(KnownField) const override; protected: IdentifierType internallyGetFieldId(KnownField field) const; @@ -86,6 +87,15 @@ inline void VorbisComment::setVendor(const TagValue &vendor) m_vendor = vendor; } +/*! + * \brief Allows multiple values for all fields. + * \remarks "Field names are not required to be unique (occur once) within a comment header." + */ +inline bool VorbisComment::supportsMultipleValues(KnownField) const +{ + return true; +} + } // namespace TagParser #endif // TAG_PARSER_VORBISCOMMENT_H