small improvements
- fixed some mostly ID3/MP3 related bugs - added convenience methods/operators
This commit is contained in:
parent
2af5ce997b
commit
899e2a97fe
|
@ -58,7 +58,7 @@ void BasicFileInfo::open(bool readOnly)
|
|||
*/
|
||||
void BasicFileInfo::reopen(bool readOnly)
|
||||
{
|
||||
close();
|
||||
invalidated();
|
||||
m_file.open(m_path, (m_readOnly = readOnly) ? ios_base::in | ios_base::binary : ios_base::in | ios_base::out | ios_base::binary);
|
||||
m_file.seekg(0, ios_base::end);
|
||||
m_size = m_file.tellg();
|
||||
|
@ -76,6 +76,14 @@ void BasicFileInfo::close()
|
|||
m_file.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Invalidates the file info manually.
|
||||
*/
|
||||
void BasicFileInfo::invalidate()
|
||||
{
|
||||
invalidated();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the current file.
|
||||
*
|
||||
|
@ -202,7 +210,7 @@ string BasicFileInfo::containingDirectory() const
|
|||
|
||||
/*!
|
||||
* \brief This function is called when the BasicFileInfo gets invalidated.
|
||||
* This is the case when the current file changes.
|
||||
* This is the case when the current file changes or is reopened.
|
||||
*
|
||||
* When subclassing and overwriting this virtual method invoke the base
|
||||
* implementation by calling BasicFileInfo::invalidated() before the reimplemented code.
|
||||
|
@ -210,7 +218,6 @@ string BasicFileInfo::containingDirectory() const
|
|||
void BasicFileInfo::invalidated()
|
||||
{
|
||||
m_size = 0;
|
||||
m_path.clear();
|
||||
close();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
bool isOpen() const;
|
||||
bool isReadOnly() const;
|
||||
void close();
|
||||
void invalidate();
|
||||
|
||||
const std::string &path() const;
|
||||
void setPath(const std::string &path);
|
||||
|
|
|
@ -123,7 +123,7 @@ void Id3v1Tag::make(ostream &stream)
|
|||
// track
|
||||
try {
|
||||
if(!m_trackPos.isEmpty() && m_trackPos.type() == TagDataType::PositionInSet)
|
||||
buffer[1] = m_trackPos.toPositionIntSet().position();
|
||||
buffer[1] = m_trackPos.toPositionInSet().position();
|
||||
} catch(ConversionException &) {
|
||||
addNotification(NotificationType::Warning, "Track position field can not be set because given value can not be converted appropriately.", context);
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ int parseGenreIndex(const stringtype &denotation, bool isBigEndian = false)
|
|||
void Id3v2Frame::parse(BinaryReader &reader, const uint32 version, const uint32 maximalSize)
|
||||
{
|
||||
invalidateStatus();
|
||||
clear();
|
||||
string context("parsing ID3v2 frame");
|
||||
|
||||
// parse header
|
||||
|
@ -115,13 +116,13 @@ void Id3v2Frame::parse(BinaryReader &reader, const uint32 version, const uint32
|
|||
// parse header for ID3v2.1 and ID3v2.2
|
||||
// -> read ID
|
||||
setId(reader.readUInt24BE());
|
||||
if((id() & 0xFFFF0000u) == 0) {
|
||||
if(id() & 0xFFFF0000u) {
|
||||
m_padding = false;
|
||||
} else {
|
||||
// padding reached
|
||||
m_padding = true;
|
||||
addNotification(NotificationType::Debug, "Frame ID starts with null-byte -> padding reached.", context);
|
||||
throw NoDataFoundException();
|
||||
} else {
|
||||
m_padding = false;
|
||||
}
|
||||
|
||||
// -> update context
|
||||
|
@ -143,13 +144,13 @@ void Id3v2Frame::parse(BinaryReader &reader, const uint32 version, const uint32
|
|||
// parse header for ID3v2.3 and ID3v2.4
|
||||
// -> read ID
|
||||
setId(reader.readUInt32BE());
|
||||
if((id() & 0xFF000000u) == 0) {
|
||||
if(id() & 0xFF000000u) {
|
||||
m_padding = false;
|
||||
} else {
|
||||
// padding reached
|
||||
m_padding = true;
|
||||
addNotification(NotificationType::Debug, "Frame ID starts with null-byte -> padding reached.", context);
|
||||
throw NoDataFoundException();
|
||||
} else {
|
||||
m_padding = false;
|
||||
}
|
||||
|
||||
// -> update context
|
||||
|
@ -232,7 +233,7 @@ void Id3v2Frame::parse(BinaryReader &reader, const uint32 version, const uint32
|
|||
position = PositionInSet(parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding));
|
||||
}
|
||||
value().assignPosition(position);
|
||||
} catch(ConversionException &) {
|
||||
} catch(const ConversionException &) {
|
||||
addNotification(NotificationType::Warning, "The value of track/disk position frame is not numeric and will be ignored.", context);
|
||||
}
|
||||
|
||||
|
@ -246,7 +247,7 @@ void Id3v2Frame::parse(BinaryReader &reader, const uint32 version, const uint32
|
|||
milliseconds = ConversionUtilities::stringToNumber<double>(parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding), 10);
|
||||
}
|
||||
value().assignTimeSpan(TimeSpan::fromMilliseconds(milliseconds));
|
||||
} catch (ConversionException &) {
|
||||
} catch (const ConversionException &) {
|
||||
addNotification(NotificationType::Warning, "The value of the length frame is not numeric and will be ignored.", context);
|
||||
}
|
||||
|
||||
|
@ -440,7 +441,7 @@ Id3v2FrameMaker::Id3v2FrameMaker(Id3v2Frame &frame, const byte version) :
|
|||
// just write the data
|
||||
copy(m_frame.value().dataPointer(), m_frame.value().dataPointer() + m_decompressedSize, m_data.get());
|
||||
}
|
||||
} catch(ConversionException &) {
|
||||
} catch(const ConversionException &) {
|
||||
m_frame.addNotification(NotificationType::Critical, "Assigned value can not be converted appropriately.", context);
|
||||
throw InvalidDataException();
|
||||
}
|
||||
|
@ -739,21 +740,23 @@ void Id3v2Frame::parsePicture(const char *buffer, size_t maxSize, TagValue &tagV
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Parses the comment from the specified \a buffer.
|
||||
* \brief Parses the comment/unsynchronized lyrics from the specified \a buffer.
|
||||
* \param buffer Specifies the buffer holding the picture.
|
||||
* \param dataSize Specifies the maximal number of bytes to read from the buffer.
|
||||
* \param tagValue Specifies the tag value used to store the results.
|
||||
*/
|
||||
void Id3v2Frame::parseComment(const char *buffer, size_t dataSize, TagValue &tagValue)
|
||||
{
|
||||
static const string context("parsing comment frame");
|
||||
static const string context("parsing comment/unsynchronized lyrics frame");
|
||||
const char *end = buffer + dataSize;
|
||||
if(dataSize < 6) {
|
||||
addNotification(NotificationType::Critical, "Comment frame is incomplete.", context);
|
||||
throw TruncatedDataException();
|
||||
}
|
||||
TagTextEncoding dataEncoding = parseTextEncodingByte(*buffer);
|
||||
tagValue.setLanguage(string(++buffer, 3));
|
||||
if(*(++buffer)) {
|
||||
tagValue.setLanguage(string(buffer, 3));
|
||||
}
|
||||
auto substr = parseSubstring(buffer += 3, dataSize -= 4, dataEncoding, true);
|
||||
tagValue.setDescription(string(get<0>(substr), get<1>(substr)), dataEncoding);
|
||||
if(get<2>(substr) >= end) {
|
||||
|
|
|
@ -21,7 +21,6 @@ enum KnownValue : uint32 {
|
|||
lEncoder = 0x54454e43,
|
||||
lBpm = 0x5442504d,
|
||||
lCover = 0x41504943,
|
||||
//lPerformers = 0x54504532,
|
||||
lWriter = 0x54455854,
|
||||
lLength = 0x544c454e,
|
||||
lLanguage = 0x544c414e,
|
||||
|
@ -45,7 +44,6 @@ enum KnownValue : uint32 {
|
|||
sEncoder = 0x54454e,
|
||||
sBpm = 0x544250,
|
||||
sCover = 0x504943,
|
||||
//sPerformers = 0x545032,
|
||||
sWriter = 0x545854,
|
||||
sLength = 0x544c45,
|
||||
sLanguage = 0x544c41,
|
||||
|
|
|
@ -204,7 +204,7 @@ inline LIB_EXPORT const char *icra() {
|
|||
}
|
||||
|
||||
inline LIB_EXPORT const char *dateRelease() {
|
||||
return "DATE_RELEASE";
|
||||
return "DATE_RELEASED";
|
||||
}
|
||||
inline LIB_EXPORT const char *dateRecorded() {
|
||||
return "DATE_RECORDED";
|
||||
|
|
|
@ -920,7 +920,7 @@ bool MediaFileInfo::removeAllId3v2Tags()
|
|||
*/
|
||||
Id3v2Tag *MediaFileInfo::createId3v2Tag()
|
||||
{
|
||||
if(!m_id3v2Tags.size()) {
|
||||
if(m_id3v2Tags.empty()) {
|
||||
m_id3v2Tags.emplace_back(make_unique<Id3v2Tag>());
|
||||
}
|
||||
return m_id3v2Tags.front().get();
|
||||
|
@ -1048,9 +1048,7 @@ bool MediaFileInfo::areTagsSupported() const
|
|||
|
||||
/*!
|
||||
* \brief Returns a pointer to the assigned MP4 tag or nullptr if none is assigned.
|
||||
*
|
||||
* \remarks The MediaFileInfo keeps the ownership over the returned
|
||||
* pointer. The returned MP4 tag will be destroyed when the
|
||||
* \remarks The MediaFileInfo keeps the ownership over the object which will be destroyed when the
|
||||
* MediaFileInfo is invalidated.
|
||||
*/
|
||||
Mp4Tag *MediaFileInfo::mp4Tag() const
|
||||
|
@ -1061,7 +1059,6 @@ Mp4Tag *MediaFileInfo::mp4Tag() const
|
|||
|
||||
/*!
|
||||
* \brief Returns pointers to the assigned Matroska tags.
|
||||
*
|
||||
* \remarks The MediaFileInfo keeps the ownership over the returned
|
||||
* pointers. The returned Matroska tags will be destroyed when the
|
||||
* MediaFileInfo is invalidated.
|
||||
|
@ -1077,11 +1074,20 @@ const vector<unique_ptr<MatroskaTag> > &MediaFileInfo::matroskaTags() const
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns a pointer to the first assigned Vorbis comment or nullptr if none is assigned.
|
||||
* \remarks The MediaFileInfo keeps the ownership over the object which will be destroyed when the
|
||||
* MediaFileInfo is invalidated.
|
||||
*/
|
||||
VorbisComment *MediaFileInfo::vorbisComment() const
|
||||
{
|
||||
return m_containerFormat == ContainerFormat::Ogg && m_container && m_container->tagCount() > 0 ? static_cast<OggContainer *>(m_container.get())->tags().front().get() : nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns all chapters assigned to the current file.
|
||||
*
|
||||
* \remarks The MediaFileInfo keeps the ownership over the chapters which will be
|
||||
* destroyed when the MediaFileInfo is invalidated.
|
||||
* \remarks The MediaFileInfo keeps the ownership over the object which will be destroyed when the
|
||||
* MediaFileInfo is invalidated.
|
||||
*/
|
||||
vector<AbstractChapter *> MediaFileInfo::chapters() const
|
||||
{
|
||||
|
@ -1098,9 +1104,8 @@ vector<AbstractChapter *> MediaFileInfo::chapters() const
|
|||
|
||||
/*!
|
||||
* \brief Returns all attachments assigned to the current file.
|
||||
*
|
||||
* \remarks The MediaFileInfo keeps the ownership over the attachments which will be
|
||||
* destroyed when the MediaFileInfo is invalidated.
|
||||
* \remarks The MediaFileInfo keeps the ownership over the object which will be destroyed when the
|
||||
* MediaFileInfo is invalidated.
|
||||
*/
|
||||
vector<AbstractAttachment *> MediaFileInfo::attachments() const
|
||||
{
|
||||
|
@ -1184,7 +1189,6 @@ NotificationType MediaFileInfo::worstNotificationTypeIncludingRelatedObjects() c
|
|||
/*!
|
||||
* \brief Returns the notifications of the current instance and all related
|
||||
* objects (tracks, tags, container, ...).
|
||||
*
|
||||
* \remarks The specified list is not cleared before notifications are added.
|
||||
*/
|
||||
void MediaFileInfo::gatherRelatedNotifications(NotificationList ¬ifications) const
|
||||
|
@ -1390,23 +1394,32 @@ void MediaFileInfo::makeMp3File()
|
|||
addNotifications(*tag);
|
||||
}
|
||||
|
||||
// determine padding, check whether rewrite is required
|
||||
// check whether rewrite is required
|
||||
bool rewriteRequired = isForcingRewrite() || (tagsSize > static_cast<uint32>(m_containerOffset));
|
||||
uint32 padding;
|
||||
uint32 padding = 0;
|
||||
if(!rewriteRequired) {
|
||||
// rewriting is not forced and new tag is not too big for available space
|
||||
// -> calculate new padding
|
||||
padding = static_cast<uint32>(m_containerOffset) - tagsSize;
|
||||
// check whether padding matches specifications
|
||||
// -> check whether the new padding matches specifications
|
||||
if(padding < minPadding() || padding > maxPadding()) {
|
||||
rewriteRequired = true;
|
||||
}
|
||||
}
|
||||
if(rewriteRequired) {
|
||||
// use preferred padding when rewriting
|
||||
if(makers.empty()) {
|
||||
// an ID3v2 tag shouldn't be written
|
||||
// -> can't include padding
|
||||
if(padding) {
|
||||
// but padding would be present -> need to rewrite
|
||||
padding = 0;
|
||||
rewriteRequired = true;
|
||||
}
|
||||
} else if(rewriteRequired) {
|
||||
// rewriting is forced or new ID3v2 tag is too big for available space
|
||||
// -> use preferred padding when rewriting anyways
|
||||
padding = preferredPadding();
|
||||
updateStatus("Preparing streams for rewriting ...");
|
||||
} else {
|
||||
updateStatus("Preparing streams for updating ...");
|
||||
}
|
||||
updateStatus(rewriteRequired ? "Preparing streams for rewriting ..." : "Preparing streams for updating ...");
|
||||
|
||||
// setup stream(s) for writing
|
||||
// -> define variables needed to handle output stream and backup stream (required when rewriting the file)
|
||||
|
@ -1449,7 +1462,7 @@ void MediaFileInfo::makeMp3File()
|
|||
// include padding into the last ID3v2 tag
|
||||
makers.back().make(outputStream, padding);
|
||||
} else {
|
||||
// no ID3v2 tags assigned -> just write padding
|
||||
// just write padding
|
||||
for(; padding; --padding) {
|
||||
outputStream.put(0);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ class MatroskaTag;
|
|||
class AbstractTrack;
|
||||
class WaveAudioStream;
|
||||
class MpegAudioFrameStream;
|
||||
class VorbisComment;
|
||||
|
||||
enum class MediaType;
|
||||
enum class TagType : unsigned int;
|
||||
|
@ -117,6 +118,7 @@ public:
|
|||
std::vector<Tag *> tags() const;
|
||||
Mp4Tag *mp4Tag() const;
|
||||
const std::vector<std::unique_ptr<MatroskaTag> > &matroskaTags() const;
|
||||
VorbisComment *vorbisComment() const;
|
||||
bool areTagsSupported() const;
|
||||
|
||||
// methods to create/remove tags
|
||||
|
|
|
@ -771,7 +771,7 @@ calculatePadding:
|
|||
reset();
|
||||
try {
|
||||
parseTracks();
|
||||
} catch(Failure &) {
|
||||
} catch(const Failure &) {
|
||||
addNotification(NotificationType::Critical, "Unable to reparse the header of the new file.", context);
|
||||
throw;
|
||||
}
|
||||
|
@ -931,11 +931,11 @@ void Mp4Container::updateOffsets(const std::vector<int64> &oldMdatOffsets, const
|
|||
addNotification(NotificationType::Warning, "traf atom stores multiple tfhd atoms but it should only contain exactly one tfhd atom.", context);
|
||||
}
|
||||
}
|
||||
} catch(Failure &) {
|
||||
} catch(const Failure &) {
|
||||
addNotification(NotificationType::Critical, "Unable to parse childs of top-level atom moof.", context);
|
||||
}
|
||||
}
|
||||
} catch(Failure &) {
|
||||
} catch(const Failure &) {
|
||||
addNotification(NotificationType::Critical, "Unable to parse top-level atom moof.", context);
|
||||
}
|
||||
// update each track
|
||||
|
@ -946,7 +946,7 @@ void Mp4Container::updateOffsets(const std::vector<int64> &oldMdatOffsets, const
|
|||
if(!track->isHeaderValid()) {
|
||||
try {
|
||||
track->parseHeader();
|
||||
} catch(Failure &) {
|
||||
} catch(const Failure &) {
|
||||
addNotification(NotificationType::Warning, "The chunk offsets of track " + track->name() + " couldn't be updated because the track seems to be invalid..", context);
|
||||
throw;
|
||||
}
|
||||
|
@ -954,7 +954,7 @@ void Mp4Container::updateOffsets(const std::vector<int64> &oldMdatOffsets, const
|
|||
if(track->isHeaderValid()) {
|
||||
try {
|
||||
track->updateChunkOffsets(oldMdatOffsets, newMdatOffsets);
|
||||
} catch(Failure &) {
|
||||
} catch(const Failure &) {
|
||||
addNotification(NotificationType::Warning, "The chunk offsets of track " + track->name() + " couldn't be updated.", context);
|
||||
throw;
|
||||
}
|
||||
|
|
|
@ -455,7 +455,7 @@ Mp4TagFieldMaker::Mp4TagFieldMaker(Mp4TagField &field) :
|
|||
// track number and disk number are exceptions
|
||||
// raw data type 0 is used, information is stored as pair of unsigned integers
|
||||
case Mp4TagAtomIds::TrackPosition: case Mp4TagAtomIds::DiskPosition: {
|
||||
PositionInSet pos = m_field.value().toPositionIntSet();
|
||||
PositionInSet pos = m_field.value().toPositionInSet();
|
||||
m_writer.writeInt32BE(pos.position());
|
||||
if(pos.total() <= numeric_limits<int16>::max()) {
|
||||
m_writer.writeInt16BE(static_cast<int16>(pos.total()));
|
||||
|
|
|
@ -280,12 +280,12 @@ void OggContainer::internalMakeFile()
|
|||
}
|
||||
// clear iterator
|
||||
m_iterator = OggIterator(fileInfo().stream(), startOffset(), fileInfo().size());
|
||||
} catch(OperationAbortedException &) {
|
||||
} catch(const OperationAbortedException &) {
|
||||
addNotification(NotificationType::Information, "Rewriting file to apply new tag information has been aborted.", context);
|
||||
BackupHelper::restoreOriginalFileFromBackupFile(fileInfo().path(), backupPath, fileInfo().stream(), backupStream);
|
||||
m_iterator.setStream(fileInfo().stream());
|
||||
throw;
|
||||
} catch(ios_base::failure &ex) {
|
||||
} catch(const ios_base::failure &ex) {
|
||||
addNotification(NotificationType::Critical, "IO error occured when rewriting file to apply new tag information.\n" + string(ex.what()), context);
|
||||
BackupHelper::restoreOriginalFileFromBackupFile(fileInfo().path(), backupPath, fileInfo().stream(), backupStream);
|
||||
m_iterator.setStream(fileInfo().stream());
|
||||
|
|
|
@ -39,10 +39,10 @@ public:
|
|||
bool bytesRemaining(size_t atLeast) const;
|
||||
|
||||
operator bool() const;
|
||||
OggIterator &operator ++();
|
||||
OggIterator operator ++(int);
|
||||
OggIterator &operator --();
|
||||
OggIterator operator --(int);
|
||||
OggIterator &operator++();
|
||||
OggIterator operator++(int);
|
||||
OggIterator &operator--();
|
||||
OggIterator operator--(int);
|
||||
|
||||
private:
|
||||
bool fetchNextPage();
|
||||
|
@ -256,7 +256,7 @@ inline bool OggIterator::bytesRemaining(size_t atLeast) const
|
|||
/*!
|
||||
* \brief Increments the current position by one segment if the iterator is valid; otherwise nothing happens.
|
||||
*/
|
||||
inline OggIterator &OggIterator::operator ++()
|
||||
inline OggIterator &OggIterator::operator++()
|
||||
{
|
||||
nextSegment();
|
||||
return *this;
|
||||
|
@ -265,7 +265,7 @@ inline OggIterator &OggIterator::operator ++()
|
|||
/*!
|
||||
* \brief Increments the current position by one segment if the iterator is valid; otherwise nothing happens.
|
||||
*/
|
||||
inline OggIterator OggIterator::operator ++(int)
|
||||
inline OggIterator OggIterator::operator++(int)
|
||||
{
|
||||
OggIterator tmp = *this;
|
||||
nextSegment();
|
||||
|
@ -275,7 +275,7 @@ inline OggIterator OggIterator::operator ++(int)
|
|||
/*!
|
||||
* \brief Decrements the current position by one segment if the iterator is valid; otherwise nothing happens.
|
||||
*/
|
||||
inline OggIterator &OggIterator::operator --()
|
||||
inline OggIterator &OggIterator::operator--()
|
||||
{
|
||||
previousSegment();
|
||||
return *this;
|
||||
|
@ -284,7 +284,7 @@ inline OggIterator &OggIterator::operator --()
|
|||
/*!
|
||||
* \brief Decrements the current position by one segment if the iterator is valid; otherwise nothing happens.
|
||||
*/
|
||||
inline OggIterator OggIterator::operator --(int)
|
||||
inline OggIterator OggIterator::operator--(int)
|
||||
{
|
||||
OggIterator tmp = *this;
|
||||
previousSegment();
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
constexpr int32 position() const;
|
||||
constexpr int32 total() const;
|
||||
constexpr bool isNull() const;
|
||||
constexpr bool operator==(const PositionInSet &other) const;
|
||||
|
||||
template <typename StringType = std::string>
|
||||
StringType toString() const;
|
||||
|
@ -91,6 +92,14 @@ constexpr inline bool PositionInSet::isNull() const
|
|||
return m_position == 0 && m_total == 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether this instance equals \a other.
|
||||
*/
|
||||
constexpr inline bool PositionInSet::operator==(const PositionInSet &other) const
|
||||
{
|
||||
return m_position == other.m_position && m_total == other.m_total;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the string representation of the current PositionInSet.
|
||||
*/
|
||||
|
|
114
tagvalue.cpp
114
tagvalue.cpp
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
using namespace ConversionUtilities;
|
||||
|
@ -119,7 +120,7 @@ TagValue::TagValue(const PositionInSet &value) :
|
|||
TagValue::TagValue(const TagValue &other) :
|
||||
m_size(other.m_size),
|
||||
m_type(other.m_type),
|
||||
m_dec(other.m_dec),
|
||||
m_desc(other.m_desc),
|
||||
m_mimeType(other.m_mimeType),
|
||||
m_lng(other.m_lng),
|
||||
m_labeledAsReadonly(other.m_labeledAsReadonly),
|
||||
|
@ -140,7 +141,7 @@ TagValue &TagValue::operator=(const TagValue &other)
|
|||
if(this != &other) {
|
||||
m_size = other.m_size;
|
||||
m_type = other.m_type;
|
||||
m_dec = other.m_dec;
|
||||
m_desc = other.m_desc;
|
||||
m_mimeType = other.m_mimeType;
|
||||
m_lng = other.m_lng;
|
||||
m_labeledAsReadonly = other.m_labeledAsReadonly;
|
||||
|
@ -156,6 +157,45 @@ TagValue &TagValue::operator=(const TagValue &other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether both instances are equal.
|
||||
*
|
||||
* Both instances are only considered equal, if the data type, encodings (if relevant for the type) and meta data are equal.
|
||||
*/
|
||||
bool TagValue::operator==(const TagValue &other) const
|
||||
{
|
||||
if(m_type != other.m_type || m_desc != other.m_desc || (!m_desc.empty() && m_descEncoding != other.m_descEncoding)
|
||||
|| m_mimeType != other.m_mimeType || m_lng != other.m_lng || m_labeledAsReadonly != other.m_labeledAsReadonly) {
|
||||
return false;
|
||||
}
|
||||
switch(m_type) {
|
||||
case TagDataType::Text:
|
||||
if(m_size != other.m_size && m_encoding != other.m_encoding) {
|
||||
return false;
|
||||
}
|
||||
return strncmp(m_ptr.get(), other.m_ptr.get(), m_size) == 0;
|
||||
case TagDataType::PositionInSet:
|
||||
return toPositionInSet() == other.toPositionInSet();
|
||||
case TagDataType::Integer:
|
||||
return toInteger() == other.toInteger();
|
||||
case TagDataType::StandardGenreIndex:
|
||||
return toStandardGenreIndex() == other.toStandardGenreIndex();
|
||||
case TagDataType::TimeSpan:
|
||||
return toTimeSpan() == other.toTimeSpan();
|
||||
case TagDataType::DateTime:
|
||||
return toDateTime() == other.toDateTime();
|
||||
case TagDataType::Picture:
|
||||
case TagDataType::Binary:
|
||||
case TagDataType::Undefined:
|
||||
if(m_size != other.m_size) {
|
||||
return false;
|
||||
}
|
||||
return strncmp(m_ptr.get(), other.m_ptr.get(), m_size) == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Destroys the TagValue.
|
||||
*/
|
||||
|
@ -171,7 +211,7 @@ TagValue::~TagValue()
|
|||
*/
|
||||
void TagValue::clearMetadata()
|
||||
{
|
||||
m_dec.clear();
|
||||
m_desc.clear();
|
||||
m_mimeType.clear();
|
||||
m_lng.clear();
|
||||
m_labeledAsReadonly = false;
|
||||
|
@ -260,7 +300,7 @@ int TagValue::toStandardGenreIndex() const
|
|||
* PositionInSet representation.
|
||||
* \throws Throws ConversionException an failure.
|
||||
*/
|
||||
PositionInSet TagValue::toPositionIntSet() const
|
||||
PositionInSet TagValue::toPositionInSet() const
|
||||
{
|
||||
if(!isEmpty()) {
|
||||
switch(m_type) {
|
||||
|
@ -293,7 +333,7 @@ TimeSpan TagValue::toTimeSpan() const
|
|||
if(!isEmpty()) {
|
||||
switch(m_type) {
|
||||
case TagDataType::Text:
|
||||
return TimeSpan::fromSeconds(ConversionUtilities::stringToNumber<int64>(string(m_ptr.get(), m_size)));
|
||||
return TimeSpan::fromString(string(m_ptr.get(), m_size));
|
||||
case TagDataType::Integer:
|
||||
case TagDataType::TimeSpan:
|
||||
switch(m_size) {
|
||||
|
@ -366,7 +406,7 @@ void TagValue::toString(string &result) const
|
|||
result = ConversionUtilities::numberToString(toInteger());
|
||||
return;
|
||||
case TagDataType::PositionInSet:
|
||||
result = toPositionIntSet().toString();
|
||||
result = toPositionInSet().toString();
|
||||
return;
|
||||
case TagDataType::StandardGenreIndex:
|
||||
if(const char *genreName = Id3Genres::stringFromIndex(toInteger())) {
|
||||
|
@ -386,6 +426,56 @@ void TagValue::toString(string &result) const
|
|||
result.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Converts the value of the current TagValue object to its equivalent
|
||||
* std::wstring representation.
|
||||
* \throws Throws ConversionException on failure.
|
||||
* \remarks Use this only, if UTF-16 text is assigned.
|
||||
*/
|
||||
u16string TagValue::toWString() const
|
||||
{
|
||||
u16string res;
|
||||
toWString(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Converts the value of the current TagValue object to its equivalent
|
||||
* std::u16string representation.
|
||||
* \throws Throws ConversionException on failure.
|
||||
* \remarks Use this only, if UTF-16 text is assigned.
|
||||
*/
|
||||
void TagValue::toWString(u16string &result) const
|
||||
{
|
||||
if(!isEmpty()) {
|
||||
switch(m_type) {
|
||||
case TagDataType::Text:
|
||||
result.assign(reinterpret_cast<typename u16string::value_type *>(m_ptr.get()), m_size / sizeof(typename u16string::value_type));
|
||||
return;
|
||||
case TagDataType::Integer:
|
||||
result = ConversionUtilities::numberToString<int32, u16string>(toInteger());
|
||||
return;
|
||||
case TagDataType::PositionInSet:
|
||||
result = toPositionInSet().toString<u16string>();
|
||||
return;
|
||||
case TagDataType::StandardGenreIndex:
|
||||
if(const char *genreName = Id3Genres::stringFromIndex(toInteger())) {
|
||||
// TODO: implement this
|
||||
throw ConversionException("Wide default genre strings are currently not supported.");
|
||||
} else {
|
||||
throw ConversionException("No string representation for the assigned standard genre index available.");
|
||||
}
|
||||
break;
|
||||
case TagDataType::TimeSpan:
|
||||
// TODO: implement this
|
||||
throw ConversionException("Wide time span string representations are currently not supported.");
|
||||
default:
|
||||
throw ConversionException("Can not convert binary data/picture to string.");
|
||||
}
|
||||
}
|
||||
result.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Assigns a copy of the given \a text.
|
||||
* \param text Specifies the text to be assigned.
|
||||
|
@ -438,15 +528,17 @@ void TagValue::assignStandardGenreIndex(int index)
|
|||
*/
|
||||
void TagValue::assignData(const char *data, size_t length, TagDataType type, TagTextEncoding encoding)
|
||||
{
|
||||
m_size = length;
|
||||
m_type = type;
|
||||
m_encoding = encoding;
|
||||
if(m_size > 0) {
|
||||
m_ptr.reset(new char[m_size]);
|
||||
if(length > m_size) {
|
||||
m_ptr = make_unique<char[]>(length);
|
||||
}
|
||||
if(length) {
|
||||
std::copy(data, data + length, m_ptr.get());
|
||||
} else {
|
||||
m_ptr.reset();
|
||||
}
|
||||
m_size = length;
|
||||
m_type = type;
|
||||
m_encoding = encoding;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
11
tagvalue.h
11
tagvalue.h
|
@ -75,6 +75,7 @@ public:
|
|||
// operators
|
||||
TagValue &operator=(const TagValue &other);
|
||||
TagValue &operator=(TagValue &&other) = default;
|
||||
bool operator==(const TagValue &other) const;
|
||||
|
||||
// methods
|
||||
bool isEmpty() const;
|
||||
|
@ -84,9 +85,11 @@ public:
|
|||
TagDataType type() const;
|
||||
std::string toString() const;
|
||||
void toString(std::string &result) const;
|
||||
std::u16string toWString() const;
|
||||
void toWString(std::u16string &result) const;
|
||||
int32 toInteger() const;
|
||||
int toStandardGenreIndex() const;
|
||||
PositionInSet toPositionIntSet() const;
|
||||
PositionInSet toPositionInSet() const;
|
||||
ChronoUtilities::TimeSpan toTimeSpan() const;
|
||||
ChronoUtilities::DateTime toDateTime() const;
|
||||
size_t dataSize() const;
|
||||
|
@ -118,7 +121,7 @@ private:
|
|||
std::unique_ptr<char[]> m_ptr;
|
||||
std::string::size_type m_size;
|
||||
TagDataType m_type;
|
||||
std::string m_dec;
|
||||
std::string m_desc;
|
||||
std::string m_mimeType;
|
||||
std::string m_lng;
|
||||
bool m_labeledAsReadonly;
|
||||
|
@ -212,7 +215,7 @@ inline char *TagValue::dataPointer() const
|
|||
*/
|
||||
inline const std::string &TagValue::description() const
|
||||
{
|
||||
return m_dec;
|
||||
return m_desc;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -225,7 +228,7 @@ inline const std::string &TagValue::description() const
|
|||
*/
|
||||
inline void TagValue::setDescription(const std::string &value, TagTextEncoding encoding)
|
||||
{
|
||||
m_dec = value;
|
||||
m_desc = value;
|
||||
m_descEncoding = encoding;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue