Tag Parser 11.0.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
id3v2tag.cpp
Go to the documentation of this file.
1#include "./id3v2tag.h"
2#include "./id3v2frameids.h"
3
4#include "../diagnostics.h"
5#include "../exceptions.h"
6
7#include <c++utilities/conversion/stringbuilder.h>
8#include <c++utilities/conversion/stringconversion.h>
9
10#include <iostream>
11
12using namespace std;
13using namespace CppUtilities;
14
15namespace TagParser {
16
27{
28 switch (field) {
38 case KnownField::Bpm:
47 return m_majorVersion > 3;
53 return true;
54 default:
55 return false;
56 }
57}
58
60{
61 const auto encoding = proposedTextEncoding();
62 for (auto &field : fields()) {
63 auto &value = field.second.value();
64 value.convertDataEncoding(encoding);
66 }
67}
68
72void Id3v2Tag::internallyGetValuesFromField(const Id3v2Tag::FieldType &field, std::vector<const TagValue *> &values) const
73{
74 if (!field.value().isEmpty()) {
75 values.emplace_back(&field.value());
76 }
77 for (const auto &value : field.additionalValues()) {
78 if (!value.isEmpty()) {
79 values.emplace_back(&value);
80 }
81 }
82}
83
90bool Id3v2Tag::internallySetValues(const IdentifierType &id, const std::vector<TagValue> &values)
91{
92 // use default implementation for non-text frames
95 }
96
97 // find existing text frame
98 auto range = fields().equal_range(id);
99 auto frameIterator = range.first;
100
101 // use existing frame or insert new text frame
102 auto valuesIterator = values.cbegin();
103 if (frameIterator != range.second) {
104 ++range.first;
105 // add primary value to existing frame
106 if (valuesIterator != values.cend()) {
107 frameIterator->second.setValue(*valuesIterator);
108 ++valuesIterator;
109 } else {
110 frameIterator->second.value().clearDataAndMetadata();
111 }
112 } else {
113 // skip if there is no existing frame but also no values to be assigned
114 if (valuesIterator == values.cend()) {
115 return true;
116 }
117 // add primary value to new frame
118 frameIterator = fields().insert(make_pair(id, Id3v2Frame(id, *valuesIterator)));
119 ++valuesIterator;
120 }
121
122 // add additional values to frame
123 frameIterator->second.additionalValues() = vector<TagValue>(valuesIterator, values.cend());
124
125 // remove remaining existing values (there are more existing values than specified ones)
126 for (; range.first != range.second; ++range.first) {
127 range.first->second.setValue(TagValue());
128 }
129 return true;
130}
131
133{
134 using namespace Id3v2FrameIds;
135 if (m_majorVersion >= 3) {
136 switch (field) {
138 return lAlbum;
140 return lArtist;
142 return lComment;
144 return lRecordingTime; // (de)serializer takes to convert to/from lYear/lRecordingDates/lDate/lTime
146 return lReleaseTime;
148 return lTitle;
150 return lGenre;
152 return lTrackPosition;
154 return lDiskPosition;
156 return lEncoder;
157 case KnownField::Bpm:
158 return lBpm;
160 return lCover;
162 return lWriter;
164 return lLength;
166 return lLanguage;
168 return lEncoderSettings;
172 return lSynchronizedLyrics;
176 return lRecordLabel;
178 return lComposer;
180 return lRating;
182 return lAlbumArtist;
183 default:;
184 }
185 } else {
186 switch (field) {
188 return sAlbum;
190 return sArtist;
192 return sComment;
194 return lRecordingTime; // (de)serializer takes to convert to/from sYear/sRecordingDates/sDate/sTime
196 return sTitle;
198 return sGenre;
200 return sTrackPosition;
202 return sDiskPosition;
204 return sEncoder;
205 case KnownField::Bpm:
206 return sBpm;
208 return sCover;
210 return sWriter;
212 return sLength;
214 return sLanguage;
216 return sEncoderSettings;
220 return sSynchronizedLyrics;
224 return sRecordLabel;
226 return sComposer;
228 return sRating;
230 return sAlbumArtist;
231 default:;
232 }
233 }
234 return 0;
235}
236
237KnownField Id3v2Tag::internallyGetKnownField(const IdentifierType &id) const
238{
239 using namespace Id3v2FrameIds;
240 switch (id) {
241 case lAlbum:
242 return KnownField::Album;
243 case lArtist:
244 return KnownField::Artist;
245 case lComment:
246 return KnownField::Comment;
247 case lRecordingTime:
248 case lYear:
250 case lTitle:
251 return KnownField::Title;
252 case lGenre:
253 return KnownField::Genre;
254 case lTrackPosition:
256 case lDiskPosition:
258 case lEncoder:
259 return KnownField::Encoder;
260 case lBpm:
261 return KnownField::Bpm;
262 case lCover:
263 return KnownField::Cover;
264 case lWriter:
266 case lLanguage:
268 case lLength:
269 return KnownField::Length;
270 case lEncoderSettings:
273 return KnownField::Lyrics;
276 case lAlbumArtist:
280 case lRecordLabel:
282 case sAlbum:
283 return KnownField::Album;
284 case sArtist:
285 return KnownField::Artist;
286 case sComment:
287 return KnownField::Comment;
288 case sYear:
290 case sTitle:
291 return KnownField::Title;
292 case sGenre:
293 return KnownField::Genre;
294 case sTrackPosition:
296 case sEncoder:
297 return KnownField::Encoder;
298 case sBpm:
299 return KnownField::Bpm;
300 case sCover:
301 return KnownField::Cover;
302 case sWriter:
304 case sLanguage:
306 case sLength:
307 return KnownField::Length;
308 case sEncoderSettings:
311 return KnownField::Lyrics;
314 case sAlbumArtist:
316 case sRecordLabel:
318 default:
319 return KnownField::Invalid;
320 }
321}
322
324{
325 using namespace Id3v2FrameIds;
326 switch (id) {
327 case lLength:
328 case sLength:
330 case lBpm:
331 case sBpm:
332 case lYear:
333 case sYear:
335 case lTrackPosition:
336 case sTrackPosition:
337 case lDiskPosition:
339 case lCover:
340 case sCover:
342 default:
344 return TagDataType::Text;
345 } else {
347 }
348 }
349}
350
357void Id3v2Tag::convertOldRecordDateFields(const std::string &diagContext, Diagnostics &diag)
358{
359 // skip if it is a v2.4.0 tag and lRecordingTime is present
360 if (majorVersion() >= 4 && fields().find(Id3v2FrameIds::lRecordingTime) != fields().cend()) {
361 return;
362 }
363
364 // parse values of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime fields
365 bool hasAnyValue = false;
366 int year = 1, month = 1, day = 1, hour = 0, minute = 0;
367 if (const auto &v = value(Id3v2FrameIds::lYear)) {
368 hasAnyValue = true;
369 try {
370 year = v.toInteger();
371 } catch (const ConversionException &e) {
372 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse year from \"TYER\" frame: ", e.what()), diagContext);
373 }
374 }
375 if (const auto &v = value(Id3v2FrameIds::lDate)) {
376 hasAnyValue = true;
377 try {
378 auto str = v.toString();
379 if (str.size() != 4) {
380 throw ConversionException("format is not DDMM");
381 }
382 day = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
383 month = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
384 } catch (const ConversionException &e) {
385 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse month and day from \"TDAT\" frame: ", e.what()), diagContext);
386 }
387 }
388 if (const auto &v = value(Id3v2FrameIds::lTime)) {
389 hasAnyValue = true;
390 try {
391 auto str = v.toString();
392 if (str.size() != 4) {
393 throw ConversionException("format is not HHMM");
394 }
395 hour = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
396 minute = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
397 } catch (const ConversionException &e) {
398 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse hour and minute from \"TIME\" frame: ", +e.what()), diagContext);
399 }
400 }
401
402 // set the field values as DateTime
403 if (!hasAnyValue) {
404 return;
405 }
406 try {
407 setValue(Id3v2FrameIds::lRecordingTime, TagValue(DateTime::fromDateAndTime(year, month, day, hour, minute)));
408 } catch (const ConversionException &e) {
409 try {
410 // try to set at least the year
411 setValue(Id3v2FrameIds::lRecordingTime, TagValue(DateTime::fromDate(year)));
412 diag.emplace_back(DiagLevel::Critical,
413 argsToString(
414 "Unable to parse the full date of the recording. Only the 'Year' frame could be parsed; related frames failed: ", e.what()),
415 diagContext);
416 } catch (const ConversionException &) {
417 }
418 diag.emplace_back(
419 DiagLevel::Critical, argsToString("Unable to parse a valid date from the 'Year' frame and related frames: ", e.what()), diagContext);
420 }
421}
422
430void Id3v2Tag::parse(istream &stream, const std::uint64_t maximalSize, Diagnostics &diag)
431{
432 // prepare parsing
433 static const string context("parsing ID3v2 tag");
434 BinaryReader reader(&stream);
435 const auto startOffset = static_cast<std::uint64_t>(stream.tellg());
436
437 // check whether the header is truncated
438 if (maximalSize && maximalSize < 10) {
439 diag.emplace_back(DiagLevel::Critical, "ID3v2 header is truncated (at least 10 bytes expected).", context);
441 }
442
443 // read signature: ID3
444 if (reader.readUInt24BE() != 0x494433u) {
445 diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
446 throw InvalidDataException();
447 }
448 // read header data
449 const std::uint8_t majorVersion = reader.readByte();
450 const std::uint8_t revisionVersion = reader.readByte();
452 m_flags = reader.readByte();
453 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
454 m_size = 10 + m_sizeExcludingHeader;
455 if (m_sizeExcludingHeader == 0) {
456 diag.emplace_back(DiagLevel::Warning, "ID3v2 tag seems to be empty.", context);
457 return;
458 }
459
460 // check if the version
461 if (!isVersionSupported()) {
462 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
464 }
465
466 // read extended header (if present)
467 if (hasExtendedHeader()) {
468 if (maximalSize && maximalSize < 14) {
469 diag.emplace_back(DiagLevel::Critical, "Extended header denoted but not present.", context);
471 }
472 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
473 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
474 diag.emplace_back(DiagLevel::Critical, "Extended header is invalid/truncated.", context);
476 }
477 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
478 }
479
480 // how many bytes remain for frames and padding?
481 std::uint32_t bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
482 if (maximalSize && bytesRemaining > maximalSize) {
483 bytesRemaining = static_cast<std::uint32_t>(maximalSize);
484 diag.emplace_back(DiagLevel::Critical, "Frames are truncated.", context);
485 }
486
487 // read frames
488 auto pos = static_cast<std::uint64_t>(stream.tellg());
489 while (bytesRemaining) {
490 // seek to next frame
491 stream.seekg(static_cast<streamoff>(pos));
492 // parse frame
493 Id3v2Frame frame;
494 try {
495 frame.parse(reader, majorVersion, bytesRemaining, diag);
496 if (Id3v2FrameIds::isTextFrame(frame.id()) && fields().count(frame.id()) == 1) {
497 diag.emplace_back(DiagLevel::Warning, "The text frame " % frame.idToString() + " exists more than once.", context);
498 }
499 fields().emplace(frame.id(), move(frame));
500 } catch (const NoDataFoundException &) {
501 if (frame.hasPaddingReached()) {
502 m_paddingSize = startOffset + m_size - pos;
503 break;
504 }
505 } catch (const Failure &) {
506 }
507
508 // calculate next frame offset
509 if (frame.totalSize() <= bytesRemaining) {
510 pos += frame.totalSize();
511 bytesRemaining -= frame.totalSize();
512 } else {
513 pos += bytesRemaining;
514 bytesRemaining = 0;
515 }
516 }
517
518 if (m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
519 convertOldRecordDateFields(context, diag);
520 }
521
522 // check for extended header
523 if (!hasFooter()) {
524 return;
525 }
526 if (maximalSize && m_size + 10 < maximalSize) {
527 // the footer does not provide additional information, just check the signature
528 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
529 if (reader.readUInt24LE() != 0x494433u) {
530 diag.emplace_back(DiagLevel::Critical, "Footer signature is invalid.", context);
531 }
532 // skip remaining footer
533 stream.seekg(7, ios_base::cur);
534 } else {
535 diag.emplace_back(DiagLevel::Critical, "Footer denoted but not present.", context);
537 }
538}
539
551{
552 return Id3v2TagMaker(*this, diag);
553}
554
562void Id3v2Tag::make(ostream &stream, std::uint32_t padding, Diagnostics &diag)
563{
564 prepareMaking(diag).make(stream, padding, diag);
565}
566
571void Id3v2Tag::setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
572{
573 m_majorVersion = majorVersion;
574 m_revisionVersion = revisionVersion;
575 m_version = argsToString('2', '.', majorVersion, '.', revisionVersion);
576}
577
590bool FrameComparer::operator()(std::uint32_t lhs, std::uint32_t rhs) const
591{
592 if (lhs == rhs) {
593 return false;
594 }
595
596 const bool lhsLong = Id3v2FrameIds::isLongId(lhs);
597 const bool rhsLong = Id3v2FrameIds::isLongId(rhs);
598 if (lhsLong != rhsLong) {
599 if (!lhsLong) {
601 if (!lhs) {
602 return true;
603 }
604 } else if (!rhsLong) {
606 if (!rhs) {
607 return true;
608 }
609 }
610 }
611
613 return true;
614 }
616 return false;
617 }
618 if (lhs == Id3v2FrameIds::lTitle || lhs == Id3v2FrameIds::sTitle) {
619 return true;
620 }
621 if (rhs == Id3v2FrameIds::lTitle || rhs == Id3v2FrameIds::sTitle) {
622 return false;
623 }
624
625 const bool lhstextfield = Id3v2FrameIds::isTextFrame(lhs);
626 const bool rhstextfield = Id3v2FrameIds::isTextFrame(rhs);
627 if (lhstextfield && !rhstextfield) {
628 return true;
629 }
630 if (!lhstextfield && rhstextfield) {
631 return false;
632 }
633
634 if (lhs == Id3v2FrameIds::lCover || lhs == Id3v2FrameIds::sCover) {
635 return false;
636 }
637 if (rhs == Id3v2FrameIds::lCover || rhs == Id3v2FrameIds::sCover) {
638 return true;
639 }
640 return lhs < rhs;
641}
642
653void Id3v2Tag::removeOldRecordDateRelatedFields()
654{
656 fields().erase(field);
657 }
658}
659
663void Id3v2Tag::prepareRecordDataForMaking(const std::string &diagContext, Diagnostics &diag)
664{
665 // get rid of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime if writing v2.4.0 or newer
666 // note: If the tag was initially v2.3.0 or older the "old" fields have already been converted to lRecordingTime when
667 // parsing and the generic accessors propose using lRecordingTime in any case.
668 if (majorVersion() >= 4) {
669 removeOldRecordDateRelatedFields();
670 return;
671 }
672
673 // convert lRecordingTime to old fields for v2.3.0 and older
674 const auto recordingTimeFieldIterator = fields().find(Id3v2FrameIds::lRecordingTime);
675 // -> If the auto-created lRecordingTime field (see note above) has been completely removed write the old fields as-is.
676 // This allows one to bypass this handling and set the old fields explicitly.
677 if (recordingTimeFieldIterator == fields().cend()) {
678 return;
679 }
680 // -> simply remove all old fields if lRecordingTime is set to an empty value
681 const auto &recordingTime = recordingTimeFieldIterator->second.value();
682 if (recordingTime.isEmpty()) {
683 removeOldRecordDateRelatedFields();
684 return;
685 }
686 // -> convert lRecordingTime (which is supposed to be an ISO string) to a DateTime
687 try {
688 const auto asDateTime = recordingTime.toDateTime();
689 // -> remove any existing old fields to avoid any leftovers
690 removeOldRecordDateRelatedFields();
691 // -> assign old fields from parsed DateTime
692 std::stringstream year, date, time;
693 year << std::setfill('0') << std::setw(4) << asDateTime.year();
694 setValue(Id3v2FrameIds::lYear, TagValue(year.str()));
695 date << std::setfill('0') << std::setw(2) << asDateTime.day() << std::setfill('0') << std::setw(2) << asDateTime.month();
696 setValue(Id3v2FrameIds::lDate, TagValue(date.str()));
697 time << std::setfill('0') << std::setw(2) << asDateTime.hour() << std::setfill('0') << std::setw(2) << asDateTime.minute();
698 setValue(Id3v2FrameIds::lTime, TagValue(time.str()));
699 if (asDateTime.second() || asDateTime.millisecond()) {
700 diag.emplace_back(DiagLevel::Warning,
701 "The recording time field (TRDA) has been truncated to full minutes when converting to corresponding fields for older ID3v2 "
702 "versions.",
703 diagContext);
704 }
705 } catch (const ConversionException &e) {
706 try {
707 diag.emplace_back(DiagLevel::Critical,
708 argsToString("Unable to convert recording time field (TRDA) with the value \"", recordingTime.toString(),
709 "\" to corresponding fields for older ID3v2 versions: ", e.what()),
710 diagContext);
711 } catch (const ConversionException &) {
712 diag.emplace_back(DiagLevel::Critical,
713 argsToString("Unable to convert recording time field (TRDA) to corresponding fields for older ID3v2 versions: ", e.what()),
714 diagContext);
715 }
716 }
717 // -> get rid of lRecordingTime
719}
720
725Id3v2TagMaker::Id3v2TagMaker(Id3v2Tag &tag, Diagnostics &diag)
726 : m_tag(tag)
727 , m_framesSize(0)
728{
729 static const string context("making ID3v2 tag");
730
731 // check if version is supported
732 // (the version could have been changed using setVersion())
733 if (!tag.isVersionSupported()) {
734 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag version isn't supported.", context);
735 throw VersionNotSupportedException();
736 }
737
738 if (m_tag.m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
739 tag.prepareRecordDataForMaking(context, diag);
740 }
741
742 // prepare frames
743 m_maker.reserve(tag.fields().size());
744 for (auto &pair : tag.fields()) {
745 try {
746 m_maker.emplace_back(pair.second.prepareMaking(tag.majorVersion(), diag));
747 m_framesSize += m_maker.back().requiredSize();
748 } catch (const Failure &) {
749 }
750 }
751
752 // calculate required size
753 // -> header + size of frames
754 m_requiredSize = 10 + m_framesSize;
755}
756
764void Id3v2TagMaker::make(std::ostream &stream, std::uint32_t padding, Diagnostics &diag)
765{
766 CPP_UTILITIES_UNUSED(diag)
767
768 BinaryWriter writer(&stream);
769
770 // write header
771 // -> signature
772 writer.writeUInt24BE(0x494433u);
773 // -> version
774 writer.writeByte(m_tag.majorVersion());
775 writer.writeByte(m_tag.revisionVersion());
776 // -> flags, but without extended header or compression bit set
777 writer.writeByte(m_tag.flags() & 0xBF);
778 // -> size (excluding header)
779 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
780
781 // write frames
782 for (auto &maker : m_maker) {
783 maker.make(writer);
784 }
785
786 // write padding
787 for (; padding; --padding) {
788 stream.put(0);
789 }
790}
791
792} // namespace TagParser
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
bool setValue(const IdentifierType &id, const TagValue &value)
Assigns the given value to the field with the specified id.
typename FieldMapBasedTagTraits< Id3v2Tag >::FieldType::IdentifierType IdentifierType
Definition: fieldbasedtag.h:36
const TagValue & value(const IdentifierType &id) const
Returns the value of the field with the specified id.
const std::multimap< IdentifierType, FieldType, Compare > & fields() const
Returns the fields of the tag by providing direct access to the field map of the tag.
bool internallySetValues(const IdentifierType &id, const std::vector< TagValue > &values)
Default implementation for setValues().
typename FieldMapBasedTagTraits< Id3v2Tag >::FieldType FieldType
Definition: fieldbasedtag.h:35
std::vector< const TagValue * > values(const IdentifierType &id) const
Returns the values of the field with the specified id.
The Id3v2Frame class is used by Id3v2Tag to store the fields.
Definition: id3v2frame.h:86
void parse(CppUtilities::BinaryReader &reader, std::uint32_t version, std::uint32_t maximalSize, Diagnostics &diag)
Parses a frame from the stream read using the specified reader.
Definition: id3v2frame.cpp:134
std::uint32_t totalSize() const
Returns the total size of the frame in bytes.
Definition: id3v2frame.h:221
bool hasPaddingReached() const
Returns whether the padding has reached.
Definition: id3v2frame.h:197
The Id3v2TagMaker class helps writing ID3v2 tags.
Definition: id3v2tag.h:35
void make(std::ostream &stream, std::uint32_t padding, Diagnostics &diag)
Saves the tag (specified when constructing the object) to the specified stream.
Definition: id3v2tag.cpp:764
std::uint8_t revisionVersion() const
Returns the revision version if known; otherwise returns 0.
Definition: id3v2tag.h:204
void make(std::ostream &targetStream, std::uint32_t padding, Diagnostics &diag)
Writes tag information to the specified stream.
Definition: id3v2tag.cpp:562
bool hasExtendedHeader() const
Returns an indication whether an extended header is used.
Definition: id3v2tag.h:239
bool isVersionSupported() const
Returns an indication whether the version is supported by the Id3v2Tag class.
Definition: id3v2tag.h:215
bool supportsMultipleValues(KnownField field) const override
Allows multiple values for some fields.
Definition: id3v2tag.cpp:26
void setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
Sets the version to the specified majorVersion and the specified revisionVersion.
Definition: id3v2tag.cpp:571
KnownField internallyGetKnownField(const IdentifierType &id) const
Definition: id3v2tag.cpp:237
std::uint8_t flags() const
Returns the flags read from the ID3v2 header.
Definition: id3v2tag.h:223
std::uint8_t majorVersion() const
Returns the major version if known; otherwise returns 0.
Definition: id3v2tag.h:196
bool hasFooter() const
Returns an indication whether a footer is present.
Definition: id3v2tag.h:255
void internallyGetValuesFromField(const FieldType &field, std::vector< const TagValue * > &values) const
Adds additional values as well.
Definition: id3v2tag.cpp:72
bool internallySetValues(const IdentifierType &id, const std::vector< TagValue > &values)
Uses default implementation for non-text frames and applies special handling to text frames.
Definition: id3v2tag.cpp:90
TagTextEncoding proposedTextEncoding() const override
Returns the proposed text encoding.
Definition: id3v2tag.h:149
TagDataType internallyGetProposedDataType(const std::uint32_t &id) const
Definition: id3v2tag.cpp:323
Id3v2TagMaker prepareMaking(Diagnostics &diag)
Prepares making.
Definition: id3v2tag.cpp:550
void ensureTextValuesAreProperlyEncoded() override
Ensures the encoding of all assigned text values is supported by the tag by converting the character ...
Definition: id3v2tag.cpp:59
IdentifierType internallyGetFieldId(KnownField field) const
Definition: id3v2tag.cpp:132
friend class Id3v2TagMaker
Definition: id3v2tag.h:80
void parse(std::istream &sourceStream, const std::uint64_t maximalSize, Diagnostics &diag)
Parses tag information from the specified stream.
Definition: id3v2tag.cpp:430
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition: exceptions.h:25
The exception that is thrown when the data to be parsed holds no parsable information (e....
Definition: exceptions.h:18
IdentifierType & id()
Returns the id of the current TagField.
std::string idToString() const
Returns the id of the current TagField as string.
The TagValue class wraps values of different types.
Definition: tagvalue.h:95
void convertDataEncoding(TagTextEncoding encoding)
Converts the currently assigned text value to the specified encoding.
Definition: tagvalue.cpp:504
bool isEmpty() const
Returns whether no or an empty value is assigned.
Definition: tagvalue.h:525
void convertDescriptionEncoding(TagTextEncoding encoding)
Converts the assigned description to use the specified encoding.
Definition: tagvalue.cpp:556
std::string m_version
Definition: tag.h:144
std::uint64_t m_size
Definition: tag.h:145
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:39
The exception that is thrown when an operation fails because the detected or specified version is not...
Definition: exceptions.h:53
constexpr bool isLongId(std::uint32_t id)
Returns an indication whether the specified id is a long frame id.
Definition: id3v2frameids.h:85
constexpr bool isTextFrame(std::uint32_t id)
Returns an indication whether the specified id is a text frame id.
TAG_PARSER_EXPORT std::uint32_t convertToLongId(std::uint32_t id)
Converts the specified short frame ID to the equivalent long frame ID.
constexpr TAG_PARSER_EXPORT std::string_view year()
constexpr TAG_PARSER_EXPORT std::string_view date()
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
KnownField
Specifies the field.
Definition: tag.h:42
TagDataType
Specifies the data type.
Definition: tagvalue.h:74
bool operator()(std::uint32_t lhs, std::uint32_t rhs) const
Returns true if lhs goes before rhs; otherwise returns false.
Definition: id3v2tag.cpp:590