#ifndef TAG_PARSER_TAG_H #define TAG_PARSER_TAG_H #include "./tagtarget.h" #include "./tagvalue.h" #include #include #include #include namespace TagParser { /*! * \brief Specifies the tag type. * * \sa Tag::type() */ enum class TagType : unsigned int { Unspecified = 0x00, /**< The tag type is unspecified. */ Id3v1Tag = 0x01, /**< The tag is a TagParser::Id3v1Tag. */ Id3v2Tag = 0x02, /**< The tag is a TagParser::Id3v2Tag. */ Mp4Tag = 0x04, /**< The tag is a TagParser::Mp4Tag. */ MatroskaTag = 0x08, /**< The tag is a TagParser::MatroskaTag. */ VorbisComment = 0x10, /**< The tag is a TagParser::VorbisComment. */ OggVorbisComment = 0x20 /**< The tag is a TagParser::OggVorbisComment. */ }; /*! * \brief Specifies the field. * * These "known" fields are used to specify a field without using * the field identifier used by the underlaying tag type. * * Not all fields are supported by all tag types (see Tag::supportsField()). * * \sa Tag::type() */ enum class KnownField : unsigned int { Invalid = std::numeric_limits::max(), /**< invalid field name, do not map this value when subclassing Tag */ Title = 0, /**< title */ Album, /**< album/collection */ Artist, /**< artist/band */ Genre, /**< genre */ Year, /**< year */ Comment, /**< comment */ Bpm, /**< beats per minute */ Bps, /**< beats per second */ Lyricist, /**< lyricist */ TrackPosition, /**< track/part number and total track/part count */ DiskPosition, /**< disk number and total disk count */ PartNumber, /**< track/part number */ TotalParts, /**< total track/part count */ Encoder, /**< encoder */ RecordDate, /**< record date */ Performers, /**< performers */ Length, /**< length */ Language, /**< language */ EncoderSettings, /**< encoder settings */ Lyrics, /**< lyrics */ SynchronizedLyrics, /**< synchronized lyrics */ Grouping, /**< grouping */ RecordLabel, /**< record label */ Cover, /**< cover */ Composer, /**< composer */ Rating, /**< rating */ Description, /**< description */ Vendor /**< vendor */ }; /*! * \brief The first valid entry in the TagParser::KnownField enum. */ constexpr KnownField firstKnownField = KnownField::Title; /*! * \brief The last valid entry in the TagParser::KnownField enum. */ constexpr KnownField lastKnownField = KnownField::Vendor; /*! * \brief The number of valid entries in the TagParser::KnownField enum. */ constexpr unsigned int knownFieldArraySize = static_cast(lastKnownField) + 1; /*! * \brief Returns the next known field. Returns KnownField::Invalid if there is not next field. */ constexpr KnownField nextKnownField(KnownField field) { return field == lastKnownField ? KnownField::Invalid : static_cast(static_cast(field) + 1); } class TAG_PARSER_EXPORT Tag { public: virtual ~Tag(); virtual TagType type() const; virtual const char *typeName() const; std::string toString() const; virtual TagTextEncoding proposedTextEncoding() const; virtual bool canEncodingBeUsed(TagTextEncoding encoding) const; virtual const TagValue &value(KnownField field) const = 0; virtual std::vector values(KnownField field) const; virtual bool setValue(KnownField field, const TagValue &value) = 0; virtual bool setValues(KnownField field, const std::vector &values); virtual bool hasField(KnownField field) const = 0; virtual void removeAllFields() = 0; const std::string &version() const; uint32 size() const; virtual bool supportsTarget() const; const TagTarget &target() const; void setTarget(const TagTarget &target); virtual TagTargetLevel targetLevel() const; const char *targetLevelName() const; bool isTargetingLevel(TagTargetLevel tagTargetLevel) const; std::string targetString() const; virtual unsigned int fieldCount() const = 0; virtual bool supportsField(KnownField field) const = 0; virtual TagDataType proposedDataType(KnownField field) const; virtual bool supportsDescription(KnownField field) const; virtual bool supportsMimeType(KnownField field) const; virtual unsigned int insertValues(const Tag &from, bool overwrite); virtual void ensureTextValuesAreProperlyEncoded() = 0; protected: Tag(); std::string m_version; uint32 m_size; TagTarget m_target; }; /*! * \brief Returns the type of the tag as TagParser::TagType. * * This is TagType::Unspecified by default and might be overwritten * when subclassing. */ inline TagType Tag::type() const { return TagType::Unspecified; } /*! * \brief Returns the type name of the tag as C-style string. * * This is "unspecified" by default and might be overwritten * when subclassing. */ inline const char *Tag::typeName() const { return "unspecified"; } /*! * \brief Returns the proposed text encoding. * * This is TagTextEncoding::Latin1 by default an might be * overwritten when subclassing. * * The tag class and its subclasses do not perform any conversions. * You have to provide all string values using an encoding which is * appropriate for the specific tag type. This method returns such * an encoding. * * \sa canEncodingBeUsed() */ inline TagTextEncoding Tag::proposedTextEncoding() const { return TagTextEncoding::Latin1; } /*! * \brief Returns an indication whether the specified \a encoding * can be used to provide string values for the tag. * * Only the proposedTextEncoding() is accepted by default. This might * be overwritten when subclassing. * * The tag class and its subclasses do not perform any conversions. * You have to provide all string values using an encoding which is * appropriate for the specific tag type. This method is meant to * determine if a particular \a encoding can be used. * * \sa canEncodingBeUsed() */ inline bool Tag::canEncodingBeUsed(TagTextEncoding encoding) const { return encoding == proposedTextEncoding(); } /*! * \brief Returns the version of the tag as std::string. * The version denotation depends on the tag type. */ inline const std::string &Tag::version() const { return m_version; } /*! * \brief Returns the size of the tag in bytes. * The tag needs to be parsed before. */ inline uint32 Tag::size() const { return m_size; } /*! * \brief Returns an indication whether a target is supported by the tag. * * If no target is supported, setting a target using setTarget() * has no effect when saving the tag. * * Most tag types don't support this feature so the default implementation * returns always false. This might be overwritten when subclassing. */ inline bool Tag::supportsTarget() const { return false; } /*! * \brief Returns the target of tag. * * \sa supportsTarget() * \sa setTarget() */ inline const TagTarget &Tag::target() const { return m_target; } /*! * \brief Sets the target of tag. * * Most tag types don't support this feature so setting * the target has no effect when saving the file. * * \sa supportsTarget() * \sa target() */ inline void Tag::setTarget(const TagTarget &target) { m_target = target; } /*! * \brief Returns the name of the current tag target level. * \remarks Returns TagTargetLevel::Unspecified if target levels are not supported by the tag. */ inline TagTargetLevel Tag::targetLevel() const { return TagTargetLevel::Unspecified; } /*! * \brief Returns the name of the current target level. * \remarks Returns nullptr if target levels are not supported by the tag. */ inline const char *Tag::targetLevelName() const { return supportsTarget() ? tagTargetLevelName(targetLevel()) : nullptr; } /*! * \brief Returns whether the tag is targeting the specified \a tagTargetLevel. * \remarks If targets are not supported by the tag it is considered targeting * everything and hence this method returns always true in this case. */ inline bool Tag::isTargetingLevel(TagTargetLevel tagTargetLevel) const { return !supportsTarget() || static_cast(targetLevel()) >= static_cast(tagTargetLevel); } /*! * \brief Returns the string representation for the assigned tag target. */ inline std::string Tag::targetString() const { return target().toString(targetLevel()); } /*! * \brief Returns the proposed data type for the specified \a field as TagDataType. * * Most values need to be provided as string (see proposedTextEncoding() and * canEncodingBeUsed()). Other values need to be provided as integer or an other * TagDataType. This method helps to determine which type is required for a particular * \a field. The tag class and its subclasses try to convert the provided values. * Nevertheless it is recommend to use the proposed data types to avoid conversion * failures. * * The default implementation returns a data type which is most commonly used for * the specified \a field. The default implementation might be overwritten when * subclassing. */ inline TagDataType Tag::proposedDataType(KnownField field) const { switch (field) { case KnownField::Bpm: case KnownField::Bps: case KnownField::Rating: case KnownField::PartNumber: case KnownField::TotalParts: return TagDataType::Integer; case KnownField::Cover: return TagDataType::Picture; case KnownField::Length: return TagDataType::TimeSpan; case KnownField::TrackPosition: case KnownField::DiskPosition: return TagDataType::PositionInSet; case KnownField::Genre: return TagDataType::StandardGenreIndex; default: return TagDataType::Text; } } /*! * \brief Returns an indications whether the specified field supports descriptions. * * If you assign a description to a field value and the field does not support * descriptions the description is ignored when saving the tag. * * The default implementation returns false for all fields. This might be overwritten * when subclassing. */ inline bool Tag::supportsDescription(KnownField) const { return false; } /*! * \brief Returns an indications whether the specified field supports mime types. * * If you assign a mime types to a field value and the field does not support * mime types the mime type is ignored when saving the tag. * * The default implementation returns false for all fields. This might be overwritten * when subclassing. */ inline bool Tag::supportsMimeType(KnownField) const { return false; } } // namespace TagParser #endif // TAG_PARSER_TAG_H