Tag Parser 12.5.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
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