Tag Parser 12.5.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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#include "../mediafileinfo.h"
7
8#include <c++utilities/conversion/stringbuilder.h>
9#include <c++utilities/conversion/stringconversion.h>
10
11#include <iostream>
12
13using namespace std;
14using namespace CppUtilities;
15
16namespace TagParser {
17
22
30
38{
40 return m_majorVersion > 3;
41 }
42 using namespace Id3v2FrameIds;
43 switch (id) {
44 case sUserDefinedText:
45 case lUserDefinedText:
46 case sUserDefinedURL:
47 case lUserDefinedURL:
48 case sRating:
49 case lRating:
50 case sComment:
51 case lComment:
52 case sCover:
53 case lCover:
54 case sUnsynchronizedLyrics:
55 case lUnsynchronizedLyrics:
56 case sSynchronizedLyrics:
57 case lSynchronizedLyrics:
58 case lPublisherWebpage:
59 return true;
60 default:
61 return false;
62 }
63}
64
66{
67 const auto encoding = proposedTextEncoding();
68 for (auto &field : fields()) {
69 auto &value = field.second.value();
70 value.convertDataEncoding(encoding);
71 value.convertDescriptionEncoding(encoding);
72 }
73}
74
78void Id3v2Tag::internallyGetValuesFromField(const Id3v2Tag::FieldType &field, std::vector<const TagValue *> &values) const
79{
80 if (!field.value().isEmpty()) {
81 values.emplace_back(&field.value());
82 }
83 for (const auto &value : field.additionalValues()) {
84 if (!value.isEmpty()) {
85 values.emplace_back(&value);
86 }
87 }
88}
89
96bool Id3v2Tag::internallySetValues(const IdentifierType &id, const std::vector<TagValue> &values)
97{
98 // use default implementation for non-text frames
101 }
102
103 // find existing text frame
104 auto range = fields().equal_range(id);
105 auto frameIterator = range.first;
106
107 // use existing frame or insert new text frame
108 auto valuesIterator = values.cbegin();
109 if (frameIterator != range.second) {
110 ++range.first;
111 // add primary value to existing frame
112 if (valuesIterator != values.cend()) {
113 frameIterator->second.setValue(*valuesIterator);
114 ++valuesIterator;
115 } else {
116 frameIterator->second.value().clearDataAndMetadata();
117 }
118 } else {
119 // skip if there is no existing frame but also no values to be assigned
120 if (valuesIterator == values.cend()) {
121 return true;
122 }
123 // add primary value to new frame
124 frameIterator = fields().insert(make_pair(id, Id3v2Frame(id, *valuesIterator)));
125 ++valuesIterator;
126 }
127
128 // add additional values to frame
129 frameIterator->second.additionalValues() = vector<TagValue>(valuesIterator, values.cend());
130
131 // remove remaining existing values (there are more existing values than specified ones)
132 for (; range.first != range.second; ++range.first) {
133 range.first->second.setValue(TagValue());
134 }
135 return true;
136}
137
139{
140 using namespace Id3v2FrameIds;
141 if (m_majorVersion >= 3) {
142 switch (field) {
144 return lAlbum;
146 return lArtist;
148 return lComment;
150 return lRecordingTime; // (de)serializer converts to/from lYear/lRecordingDates/lDate/lTime
152 return lReleaseTime;
154 return lTitle;
156 return lGenre;
158 return lTrackPosition;
160 return lDiskPosition;
162 return lEncoder;
163 case KnownField::Bpm:
164 return lBpm;
166 return lCover;
168 return lWriter;
170 return lLength;
172 return lLanguage;
174 return lEncoderSettings;
176 return lUnsynchronizedLyrics;
178 return lSynchronizedLyrics;
180 return lContentGroupDescription;
182 return lRecordLabel;
184 return lComposer;
186 return lPlayCounter;
188 return lRating;
190 return lAlbumArtist;
192 return lRemixedBy;
194 return lCopyright;
196 return lTaggingTime;
198 return lEncodingTime;
200 return lOriginalReleaseTime;
202 return lOriginalMediaType;
203 case KnownField::Mood:
204 return lMood;
206 return lKey;
208 return lPublisherWebpage;
210 return lPerformerWebpage;
212 return lPaymentWebpage;
214 return lSubtitleOrDescriptionRefinement;
216 return lLongDescription;
217 default:;
218 }
219 } else {
220 switch (field) {
222 return sAlbum;
224 return sArtist;
226 return sComment;
228 return lRecordingTime; // (de)serializer converts to/from sYear/sRecordingDates/sDate/sTime
230 return sTitle;
232 return sGenre;
234 return sTrackPosition;
236 return sDiskPosition;
238 return sEncoder;
239 case KnownField::Bpm:
240 return sBpm;
242 return sCover;
244 return sWriter;
246 return sLength;
248 return sLanguage;
250 return sEncoderSettings;
252 return sUnsynchronizedLyrics;
254 return sSynchronizedLyrics;
256 return sContentGroupDescription;
258 return sRecordLabel;
260 return sComposer;
262 return sPlayCounter;
264 return sRating;
266 return sAlbumArtist;
268 return sRemixedBy;
270 return sCopyright;
272 return sOriginalMediaType;
274 return sKey;
276 return sPublisherWebpage;
278 return sPerformerWebpage;
280 return sSubtitleOrDescriptionRefinement;
281 default:;
282 }
283 }
284 return 0;
285}
286
288{
289 using namespace Id3v2FrameIds;
290 switch (id) {
291 case lAlbum:
292 return KnownField::Album;
293 case lArtist:
294 return KnownField::Artist;
295 case lComment:
296 return KnownField::Comment;
297 case lRecordingTime:
298 case lYear:
300 case lTitle:
301 return KnownField::Title;
302 case lGenre:
303 return KnownField::Genre;
304 case lTrackPosition:
306 case lDiskPosition:
308 case lEncoder:
309 return KnownField::Encoder;
310 case lBpm:
311 return KnownField::Bpm;
312 case lCover:
313 return KnownField::Cover;
314 case lWriter:
316 case lLanguage:
318 case lLength:
319 return KnownField::Length;
320 case lEncoderSettings:
322 case lUnsynchronizedLyrics:
323 return KnownField::Lyrics;
324 case lSynchronizedLyrics:
326 case lAlbumArtist:
328 case lRemixedBy:
330 case lCopyright:
332 case lContentGroupDescription:
334 case lRecordLabel:
336 case lTaggingTime:
338 case lEncodingTime:
340 case lReleaseTime:
342 case lOriginalReleaseTime:
344 case lOriginalMediaType:
346 case lMood:
347 return KnownField::Mood;
348 case lPlayCounter:
350 case lRating:
351 return KnownField::Rating;
352 case lISRC:
353 return KnownField::ISRC;
354 case lKey:
356 case lComposer:
358 case lPublisherWebpage:
360 case lPerformerWebpage:
362 case lPaymentWebpage:
364 case lSubtitleOrDescriptionRefinement:
366 case lLongDescription:
368 case sAlbum:
369 return KnownField::Album;
370 case sArtist:
371 return KnownField::Artist;
372 case sComment:
373 return KnownField::Comment;
374 case sYear:
376 case sTitle:
377 return KnownField::Title;
378 case sGenre:
379 return KnownField::Genre;
380 case sTrackPosition:
382 case sDiskPosition:
384 case sEncoder:
385 return KnownField::Encoder;
386 case sBpm:
387 return KnownField::Bpm;
388 case sCover:
389 return KnownField::Cover;
390 case sWriter:
392 case sLanguage:
394 case sLength:
395 return KnownField::Length;
396 case sEncoderSettings:
398 case sUnsynchronizedLyrics:
399 return KnownField::Lyrics;
400 case sSynchronizedLyrics:
402 case sAlbumArtist:
404 case sRecordLabel:
406 case sRemixedBy:
408 case sCopyright:
410 case sPlayCounter:
412 case sRating:
413 return KnownField::Rating;
414 case sISRC:
415 return KnownField::ISRC;
416 case sOriginalMediaType:
418 case sKey:
420 case sComposer:
422 case sContentGroupDescription:
424 case sPublisherWebpage:
426 case sPerformerWebpage:
428 case sSubtitleOrDescriptionRefinement:
430 default:
431 return KnownField::Invalid;
432 }
433}
434
436{
437 using namespace Id3v2FrameIds;
438 switch (id) {
439 case lLength:
440 case sLength:
442 case lBpm:
443 case sBpm:
444 case lYear:
445 case sYear:
446 case lPlayCounter:
447 case sPlayCounter:
449 case lTrackPosition:
450 case sTrackPosition:
451 case lDiskPosition:
453 case lCover:
454 case sCover:
456 case lRating:
457 case sRating:
459 default:
461 return TagDataType::Text;
462 } else {
464 }
465 }
466}
467
474void Id3v2Tag::convertOldRecordDateFields(const std::string &diagContext, Diagnostics &diag)
475{
476 // skip if it is a v2.4.0 tag and lRecordingTime is present
477 if (majorVersion() >= 4 && fields().find(Id3v2FrameIds::lRecordingTime) != fields().cend()) {
478 return;
479 }
480
481 // parse values of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime fields
482 auto expr = DateTimeExpression();
483 auto year = 1, month = 1, day = 1, hour = 0, minute = 0;
484 if (const auto &v = value(Id3v2FrameIds::lYear)) {
485 expr.parts |= DateTimeParts::Year;
486 try {
487 year = v.toInteger();
488 } catch (const ConversionException &e) {
489 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse year from \"TYER\" frame: ", e.what()), diagContext);
490 }
491 }
492 if (const auto &v = value(Id3v2FrameIds::lDate)) {
493 expr.parts |= DateTimeParts::Day | DateTimeParts::Month;
494 try {
495 auto str = v.toString();
496 if (str.size() != 4) {
497 throw ConversionException("format is not DDMM");
498 }
499 day = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
500 month = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
501 } catch (const ConversionException &e) {
502 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse month and day from \"TDAT\" frame: ", e.what()), diagContext);
503 }
504 }
505 if (const auto &v = value(Id3v2FrameIds::lTime)) {
506 expr.parts |= DateTimeParts::Hour | DateTimeParts::Minute;
507 try {
508 auto str = v.toString();
509 if (str.size() != 4) {
510 throw ConversionException("format is not HHMM");
511 }
512 hour = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
513 minute = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
514 } catch (const ConversionException &e) {
515 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse hour and minute from \"TIME\" frame: ", +e.what()), diagContext);
516 }
517 }
518
519 // set the field values as DateTime
520 if (expr.parts == DateTimeParts::None) {
521 return;
522 }
523 try {
524 expr.value = DateTime::fromDateAndTime(year, month, day, hour, minute);
526 } catch (const ConversionException &e) {
527 try {
528 // try to set at least the year
529 expr.parts = DateTimeParts::Year;
530 expr.value = DateTime::fromDate(year);
532 diag.emplace_back(DiagLevel::Critical,
533 argsToString(
534 "Unable to parse the full date of the recording. Only the 'Year' frame could be parsed; related frames failed: ", e.what()),
535 diagContext);
536 } catch (const ConversionException &) {
537 }
538 diag.emplace_back(
539 DiagLevel::Critical, argsToString("Unable to parse a valid date from the 'Year' frame and related frames: ", e.what()), diagContext);
540 }
541}
542
550void Id3v2Tag::parse(istream &stream, const std::uint64_t maximalSize, Diagnostics &diag)
551{
552 // prepare parsing
553 static const string context("parsing ID3v2 tag");
554 BinaryReader reader(&stream);
555 const auto startOffset = static_cast<std::uint64_t>(stream.tellg());
556
557 // check whether the header is truncated
558 if (maximalSize && maximalSize < 10) {
559 diag.emplace_back(DiagLevel::Critical, "ID3v2 header is truncated (at least 10 bytes expected).", context);
561 }
562
563 // read signature: ID3
564 if (reader.readUInt24BE() != 0x494433u) {
565 diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
566 throw InvalidDataException();
567 }
568 // read header data
569 const std::uint8_t majorVersion = reader.readByte();
570 const std::uint8_t revisionVersion = reader.readByte();
572 m_flags = reader.readByte();
573 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
574 m_size = 10 + m_sizeExcludingHeader;
575 if (m_sizeExcludingHeader == 0) {
576 diag.emplace_back(DiagLevel::Warning, "ID3v2 tag seems to be empty.", context);
577 return;
578 }
579
580 // check if the version
581 if (!isVersionSupported()) {
582 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
584 }
585
586 // read extended header (if present)
587 if (hasExtendedHeader()) {
588 if (maximalSize && maximalSize < 14) {
589 diag.emplace_back(DiagLevel::Critical, "Extended header denoted but not present.", context);
591 }
592 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
593 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
594 diag.emplace_back(DiagLevel::Critical, "Extended header is invalid/truncated.", context);
596 }
597 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
598 }
599
600 // how many bytes remain for frames and padding?
601 std::uint32_t bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
602 if (maximalSize && bytesRemaining > maximalSize) {
603 bytesRemaining = static_cast<std::uint32_t>(maximalSize);
604 diag.emplace_back(DiagLevel::Critical, "Frames are truncated.", context);
605 }
606
607 // read frames
608 auto pos = static_cast<std::uint64_t>(stream.tellg());
609 while (bytesRemaining) {
610 // seek to next frame
611 stream.seekg(static_cast<streamoff>(pos));
612 // parse frame
613 Id3v2Frame frame;
614 try {
615 frame.parse(reader, majorVersion, bytesRemaining, diag);
616 if (Id3v2FrameIds::isTextFrame(frame.id()) && fields().count(frame.id()) == 1) {
617 diag.emplace_back(DiagLevel::Warning, "The text frame " % frame.idToString() + " exists more than once.", context);
618 }
619 fields().emplace(frame.id(), std::move(frame));
620 } catch (const NoDataFoundException &) {
621 if (frame.hasPaddingReached()) {
622 m_paddingSize = startOffset + m_size - pos;
623 break;
624 }
625 } catch (const Failure &) {
626 }
627
628 // calculate next frame offset
629 if (frame.totalSize() <= bytesRemaining) {
630 pos += frame.totalSize();
631 bytesRemaining -= frame.totalSize();
632 } else {
633 pos += bytesRemaining;
634 bytesRemaining = 0;
635 }
636 }
637
638 if (m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
639 convertOldRecordDateFields(context, diag);
640 }
641
642 // check for extended header
643 if (!hasFooter()) {
644 return;
645 }
646 if (maximalSize && m_size + 10 < maximalSize) {
647 // the footer does not provide additional information, just check the signature
648 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
649 if (reader.readUInt24LE() != 0x494433u) {
650 diag.emplace_back(DiagLevel::Critical, "Footer signature is invalid.", context);
651 }
652 // skip remaining footer
653 stream.seekg(7, ios_base::cur);
654 } else {
655 diag.emplace_back(DiagLevel::Critical, "Footer denoted but not present.", context);
657 }
658}
659
671{
672 return Id3v2TagMaker(*this, diag);
673}
674
682void Id3v2Tag::make(ostream &stream, std::uint32_t padding, Diagnostics &diag)
683{
684 prepareMaking(diag).make(stream, padding, diag);
685}
686
691void Id3v2Tag::setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
692{
693 m_majorVersion = majorVersion;
694 m_revisionVersion = revisionVersion;
695 m_version = argsToString('2', '.', majorVersion, '.', revisionVersion);
696}
697
704
710bool FrameComparer::operator()(std::uint32_t lhs, std::uint32_t rhs) const
711{
712 if (lhs == rhs) {
713 return false;
714 }
715
716 const bool lhsLong = Id3v2FrameIds::isLongId(lhs);
717 const bool rhsLong = Id3v2FrameIds::isLongId(rhs);
718 if (lhsLong != rhsLong) {
719 if (!lhsLong) {
721 if (!lhs) {
722 return true;
723 }
724 } else if (!rhsLong) {
726 if (!rhs) {
727 return true;
728 }
729 }
730 }
731
733 return true;
734 }
736 return false;
737 }
738 if (lhs == Id3v2FrameIds::lTitle || lhs == Id3v2FrameIds::sTitle) {
739 return true;
740 }
741 if (rhs == Id3v2FrameIds::lTitle || rhs == Id3v2FrameIds::sTitle) {
742 return false;
743 }
744
745 const bool lhstextfield = Id3v2FrameIds::isTextFrame(lhs);
746 const bool rhstextfield = Id3v2FrameIds::isTextFrame(rhs);
747 if (lhstextfield && !rhstextfield) {
748 return true;
749 }
750 if (!lhstextfield && rhstextfield) {
751 return false;
752 }
753
754 if (lhs == Id3v2FrameIds::lCover || lhs == Id3v2FrameIds::sCover) {
755 return false;
756 }
757 if (rhs == Id3v2FrameIds::lCover || rhs == Id3v2FrameIds::sCover) {
758 return true;
759 }
760 return lhs < rhs;
761}
762
769
773void Id3v2Tag::removeOldRecordDateRelatedFields()
774{
776 fields().erase(field);
777 }
778}
779
783void Id3v2Tag::prepareRecordDataForMaking(const std::string &diagContext, Diagnostics &diag)
784{
785 // get rid of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime if writing v2.4.0 or newer
786 // note: If the tag was initially v2.3.0 or older the "old" fields have already been converted to lRecordingTime when
787 // parsing and the generic accessors propose using lRecordingTime in any case.
788 if (majorVersion() >= 4) {
789 removeOldRecordDateRelatedFields();
790 return;
791 }
792
793 // convert lRecordingTime to old fields for v2.3.0 and older
794 const auto recordingTimeFieldIterator = fields().find(Id3v2FrameIds::lRecordingTime);
795 // -> If the auto-created lRecordingTime field (see note above) has been completely removed write the old fields as-is.
796 // This allows one to bypass this handling and set the old fields explicitly.
797 if (recordingTimeFieldIterator == fields().cend()) {
798 return;
799 }
800 // -> simply remove all old fields if lRecordingTime is set to an empty value
801 const auto &recordingTime = recordingTimeFieldIterator->second.value();
802 if (recordingTime.isEmpty()) {
803 removeOldRecordDateRelatedFields();
804 return;
805 }
806 // -> convert lRecordingTime (which is supposed to be an ISO string) to a DateTime
807 try {
808 const auto dateTimeExpr = recordingTime.toDateTimeExpression();
809 const auto &asDateTime = dateTimeExpr.value;
810 // -> remove any existing old fields to avoid any leftovers
811 removeOldRecordDateRelatedFields();
812 // -> assign old fields from parsed DateTime
813 std::stringstream year, date, time;
814 if (dateTimeExpr.parts & DateTimeParts::Year) {
815 year << std::setfill('0') << std::setw(4) << asDateTime.year();
816 setValue(Id3v2FrameIds::lYear, TagValue(year.str()));
817 }
818 if (dateTimeExpr.parts & (DateTimeParts::Day | DateTimeParts::Month)) {
819 date << std::setfill('0') << std::setw(2) << asDateTime.day() << std::setfill('0') << std::setw(2) << asDateTime.month();
820 setValue(Id3v2FrameIds::lDate, TagValue(date.str()));
821 }
822 if (dateTimeExpr.parts & DateTimeParts::Time) {
823 time << std::setfill('0') << std::setw(2) << asDateTime.hour() << std::setfill('0') << std::setw(2) << asDateTime.minute();
824 setValue(Id3v2FrameIds::lTime, TagValue(time.str()));
825 }
826 if (dateTimeExpr.parts & (DateTimeParts::Second | DateTimeParts::SubSecond)) {
827 diag.emplace_back(DiagLevel::Warning,
828 "The recording time field (TDRC) has been truncated to full minutes when converting to corresponding fields for older ID3v2 "
829 "versions.",
830 diagContext);
831 }
832 } catch (const ConversionException &e) {
833 try {
834 diag.emplace_back(DiagLevel::Critical,
835 argsToString("Unable to convert recording time field (TDRC) with the value \"", recordingTime.toString(),
836 "\" to corresponding fields for older ID3v2 versions: ", e.what()),
837 diagContext);
838 } catch (const ConversionException &) {
839 diag.emplace_back(DiagLevel::Critical,
840 argsToString("Unable to convert recording time field (TRDA) to corresponding fields for older ID3v2 versions: ", e.what()),
841 diagContext);
842 }
843 }
844 // -> get rid of lRecordingTime
846}
847
852Id3v2TagMaker::Id3v2TagMaker(Id3v2Tag &tag, Diagnostics &diag)
853 : m_tag(tag)
854 , m_framesSize(0)
855{
856 static const string context("making ID3v2 tag");
857
858 // check if version is supported
859 // (the version could have been changed using setVersion())
860 if (!tag.isVersionSupported()) {
861 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag version isn't supported.", context);
862 throw VersionNotSupportedException();
863 }
864
865 if (m_tag.m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
866 tag.prepareRecordDataForMaking(context, diag);
867 }
868
869 // prepare frames
870 m_maker.reserve(tag.fields().size());
871 for (auto &pair : tag.fields()) {
872 try {
873 m_maker.emplace_back(pair.second.prepareMaking(tag.majorVersion(), diag));
874 m_framesSize += m_maker.back().requiredSize();
875 } catch (const Failure &) {
876 }
877 }
878
879 // calculate required size
880 // -> header + size of frames
881 m_requiredSize = 10 + m_framesSize;
882}
883
891void Id3v2TagMaker::make(std::ostream &stream, std::uint32_t padding, Diagnostics &diag)
892{
893 CPP_UTILITIES_UNUSED(diag)
894
895 BinaryWriter writer(&stream);
896
897 // write header
898 // -> signature
899 writer.writeUInt24BE(0x494433u);
900 // -> version
901 writer.writeByte(m_tag.majorVersion());
902 writer.writeByte(m_tag.revisionVersion());
903 // -> flags, but without extended header or compression bit set
904 writer.writeByte(m_tag.flags() & 0xBF);
905 // -> size (excluding header)
906 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
907
908 // write frames
909 for (auto &maker : m_maker) {
910 maker.make(writer);
911 }
912
913 MediaFileInfo::writePadding(stream, padding);
914}
915
916} // namespace TagParser
The Diagnostics class is a container for DiagMessage.
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)
typename FieldMapBasedTagTraits< Id3v2Tag >::FieldType::IdentifierType IdentifierType
const TagValue & value(const IdentifierType &id) const
const std::multimap< IdentifierType, FieldType, Compare > & fields() const
bool internallySetValues(const IdentifierType &id, const std::vector< TagValue > &values)
typename FieldMapBasedTagTraits< Id3v2Tag >::FieldType FieldType
std::vector< const TagValue * > values(const IdentifierType &id) const
IdentifierType fieldId(KnownField value) const
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.
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
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:891
Implementation of TagParser::Tag for ID3v2 tags.
Definition id3v2tag.h:78
std::uint8_t revisionVersion() const
Returns the revision version if known; otherwise returns 0.
Definition id3v2tag.h:205
void make(std::ostream &targetStream, std::uint32_t padding, Diagnostics &diag)
Writes tag information to the specified stream.
Definition id3v2tag.cpp:682
bool hasExtendedHeader() const
Returns an indication whether an extended header is used.
Definition id3v2tag.h:240
bool isVersionSupported() const
Returns an indication whether the version is supported by the Id3v2Tag class.
Definition id3v2tag.h:216
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:691
KnownField internallyGetKnownField(const IdentifierType &id) const
Definition id3v2tag.cpp:287
std::uint8_t majorVersion() const
Returns the major version if known; otherwise returns 0.
Definition id3v2tag.h:197
bool hasFooter() const
Returns an indication whether a footer is present.
Definition id3v2tag.h:256
void internallyGetValuesFromField(const FieldType &field, std::vector< const TagValue * > &values) const
Adds additional values as well.
Definition id3v2tag.cpp:78
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:96
TagTextEncoding proposedTextEncoding() const override
Returns the proposed text encoding.
Definition id3v2tag.h:150
TagDataType internallyGetProposedDataType(const std::uint32_t &id) const
Definition id3v2tag.cpp:435
Id3v2TagMaker prepareMaking(Diagnostics &diag)
Prepares making.
Definition id3v2tag.cpp:670
void ensureTextValuesAreProperlyEncoded() override
Ensures the encoding of all assigned text values is supported by the tag by converting the character ...
Definition id3v2tag.cpp:65
IdentifierType internallyGetFieldId(KnownField field) const
Definition id3v2tag.cpp:138
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:550
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition exceptions.h:25
static void writePadding(std::ostream &outputStream, uint64_t size)
Writes the specified number of zeroes to outputStream.
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:147
std::string m_version
Definition tag.h:216
std::uint64_t m_size
Definition tag.h:217
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
Encapsulates the most common ID3v2 frame IDs and related functions.
constexpr bool isLongId(std::uint32_t id)
Returns an indication whether the specified id is a long frame id.
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:37
TagDataType
Specifies the data type.
Definition tagvalue.h:119
bool operator()(std::uint32_t lhs, std::uint32_t rhs) const
Returns true if lhs goes before rhs; otherwise returns false.
Definition id3v2tag.cpp:710