Tag Parser 12.3.1
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
28{
29 switch (field) {
39 case KnownField::Bpm:
48 return m_majorVersion > 3;
54 return true;
55 default:
56 return false;
57 }
58}
59
61{
62 const auto encoding = proposedTextEncoding();
63 for (auto &field : fields()) {
64 auto &value = field.second.value();
65 value.convertDataEncoding(encoding);
67 }
68}
69
73void Id3v2Tag::internallyGetValuesFromField(const Id3v2Tag::FieldType &field, std::vector<const TagValue *> &values) const
74{
75 if (!field.value().isEmpty()) {
76 values.emplace_back(&field.value());
77 }
78 for (const auto &value : field.additionalValues()) {
79 if (!value.isEmpty()) {
80 values.emplace_back(&value);
81 }
82 }
83}
84
91bool Id3v2Tag::internallySetValues(const IdentifierType &id, const std::vector<TagValue> &values)
92{
93 // use default implementation for non-text frames
96 }
97
98 // find existing text frame
99 auto range = fields().equal_range(id);
100 auto frameIterator = range.first;
101
102 // use existing frame or insert new text frame
103 auto valuesIterator = values.cbegin();
104 if (frameIterator != range.second) {
105 ++range.first;
106 // add primary value to existing frame
107 if (valuesIterator != values.cend()) {
108 frameIterator->second.setValue(*valuesIterator);
109 ++valuesIterator;
110 } else {
111 frameIterator->second.value().clearDataAndMetadata();
112 }
113 } else {
114 // skip if there is no existing frame but also no values to be assigned
115 if (valuesIterator == values.cend()) {
116 return true;
117 }
118 // add primary value to new frame
119 frameIterator = fields().insert(make_pair(id, Id3v2Frame(id, *valuesIterator)));
120 ++valuesIterator;
121 }
122
123 // add additional values to frame
124 frameIterator->second.additionalValues() = vector<TagValue>(valuesIterator, values.cend());
125
126 // remove remaining existing values (there are more existing values than specified ones)
127 for (; range.first != range.second; ++range.first) {
128 range.first->second.setValue(TagValue());
129 }
130 return true;
131}
132
134{
135 using namespace Id3v2FrameIds;
136 if (m_majorVersion >= 3) {
137 switch (field) {
139 return lAlbum;
141 return lArtist;
143 return lComment;
145 return lRecordingTime; // (de)serializer converts to/from lYear/lRecordingDates/lDate/lTime
147 return lReleaseTime;
149 return lTitle;
151 return lGenre;
153 return lTrackPosition;
155 return lDiskPosition;
157 return lEncoder;
158 case KnownField::Bpm:
159 return lBpm;
161 return lCover;
163 return lWriter;
165 return lLength;
167 return lLanguage;
169 return lEncoderSettings;
171 return lUnsynchronizedLyrics;
173 return lSynchronizedLyrics;
175 return lContentGroupDescription;
177 return lRecordLabel;
179 return lComposer;
181 return lPlayCounter;
183 return lRating;
185 return lAlbumArtist;
187 return lRemixedBy;
189 return lCopyright;
191 return lTaggingTime;
193 return lEncodingTime;
195 return lOriginalReleaseTime;
196 case KnownField::Mood:
197 return lMood;
198 default:;
199 }
200 } else {
201 switch (field) {
203 return sAlbum;
205 return sArtist;
207 return sComment;
209 return lRecordingTime; // (de)serializer converts to/from sYear/sRecordingDates/sDate/sTime
211 return sTitle;
213 return sGenre;
215 return sTrackPosition;
217 return sDiskPosition;
219 return sEncoder;
220 case KnownField::Bpm:
221 return sBpm;
223 return sCover;
225 return sWriter;
227 return sLength;
229 return sLanguage;
231 return sEncoderSettings;
233 return sUnsynchronizedLyrics;
235 return sSynchronizedLyrics;
237 return sContentGroupDescription;
239 return sRecordLabel;
241 return sComposer;
243 return sPlayCounter;
245 return sRating;
247 return sAlbumArtist;
249 return sRemixedBy;
251 return sCopyright;
252 default:;
253 }
254 }
255 return 0;
256}
257
258KnownField Id3v2Tag::internallyGetKnownField(const IdentifierType &id) const
259{
260 using namespace Id3v2FrameIds;
261 switch (id) {
262 case lAlbum:
263 return KnownField::Album;
264 case lArtist:
265 return KnownField::Artist;
266 case lComment:
267 return KnownField::Comment;
268 case lRecordingTime:
269 case lYear:
271 case lTitle:
272 return KnownField::Title;
273 case lGenre:
274 return KnownField::Genre;
275 case lTrackPosition:
277 case lDiskPosition:
279 case lEncoder:
280 return KnownField::Encoder;
281 case lBpm:
282 return KnownField::Bpm;
283 case lCover:
284 return KnownField::Cover;
285 case lWriter:
287 case lLanguage:
289 case lLength:
290 return KnownField::Length;
291 case lEncoderSettings:
293 case lUnsynchronizedLyrics:
294 return KnownField::Lyrics;
295 case lSynchronizedLyrics:
297 case lAlbumArtist:
299 case lRemixedBy:
301 case lCopyright:
303 case lContentGroupDescription:
305 case lRecordLabel:
307 case lTaggingTime:
309 case lEncodingTime:
311 case lOriginalReleaseTime:
313 case lMood:
314 return KnownField::Mood;
315 case lPlayCounter:
317 case lRating:
318 return KnownField::Rating;
319 case lISRC:
320 return KnownField::ISRC;
321 case sAlbum:
322 return KnownField::Album;
323 case sArtist:
324 return KnownField::Artist;
325 case sComment:
326 return KnownField::Comment;
327 case sYear:
329 case sTitle:
330 return KnownField::Title;
331 case sGenre:
332 return KnownField::Genre;
333 case sTrackPosition:
335 case sEncoder:
336 return KnownField::Encoder;
337 case sBpm:
338 return KnownField::Bpm;
339 case sCover:
340 return KnownField::Cover;
341 case sWriter:
343 case sLanguage:
345 case sLength:
346 return KnownField::Length;
347 case sEncoderSettings:
349 case sUnsynchronizedLyrics:
350 return KnownField::Lyrics;
351 case sSynchronizedLyrics:
353 case sAlbumArtist:
355 case sRecordLabel:
357 case sRemixedBy:
359 case sCopyright:
361 case sPlayCounter:
363 case sRating:
364 return KnownField::Rating;
365 case sISRC:
366 return KnownField::ISRC;
367 default:
368 return KnownField::Invalid;
369 }
370}
371
373{
374 using namespace Id3v2FrameIds;
375 switch (id) {
376 case lLength:
377 case sLength:
379 case lBpm:
380 case sBpm:
381 case lYear:
382 case sYear:
383 case lPlayCounter:
384 case sPlayCounter:
386 case lTrackPosition:
387 case sTrackPosition:
388 case lDiskPosition:
390 case lCover:
391 case sCover:
393 case lRating:
394 case sRating:
396 default:
398 return TagDataType::Text;
399 } else {
401 }
402 }
403}
404
411void Id3v2Tag::convertOldRecordDateFields(const std::string &diagContext, Diagnostics &diag)
412{
413 // skip if it is a v2.4.0 tag and lRecordingTime is present
414 if (majorVersion() >= 4 && fields().find(Id3v2FrameIds::lRecordingTime) != fields().cend()) {
415 return;
416 }
417
418 // parse values of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime fields
419 auto expr = DateTimeExpression();
420 auto year = 1, month = 1, day = 1, hour = 0, minute = 0;
421 if (const auto &v = value(Id3v2FrameIds::lYear)) {
422 expr.parts |= DateTimeParts::Year;
423 try {
424 year = v.toInteger();
425 } catch (const ConversionException &e) {
426 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse year from \"TYER\" frame: ", e.what()), diagContext);
427 }
428 }
429 if (const auto &v = value(Id3v2FrameIds::lDate)) {
430 expr.parts |= DateTimeParts::Day | DateTimeParts::Month;
431 try {
432 auto str = v.toString();
433 if (str.size() != 4) {
434 throw ConversionException("format is not DDMM");
435 }
436 day = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
437 month = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
438 } catch (const ConversionException &e) {
439 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse month and day from \"TDAT\" frame: ", e.what()), diagContext);
440 }
441 }
442 if (const auto &v = value(Id3v2FrameIds::lTime)) {
443 expr.parts |= DateTimeParts::Hour | DateTimeParts::Minute;
444 try {
445 auto str = v.toString();
446 if (str.size() != 4) {
447 throw ConversionException("format is not HHMM");
448 }
449 hour = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
450 minute = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
451 } catch (const ConversionException &e) {
452 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse hour and minute from \"TIME\" frame: ", +e.what()), diagContext);
453 }
454 }
455
456 // set the field values as DateTime
457 if (expr.parts == DateTimeParts::None) {
458 return;
459 }
460 try {
461 expr.value = DateTime::fromDateAndTime(year, month, day, hour, minute);
463 } catch (const ConversionException &e) {
464 try {
465 // try to set at least the year
466 expr.parts = DateTimeParts::Year;
467 expr.value = DateTime::fromDate(year);
469 diag.emplace_back(DiagLevel::Critical,
470 argsToString(
471 "Unable to parse the full date of the recording. Only the 'Year' frame could be parsed; related frames failed: ", e.what()),
472 diagContext);
473 } catch (const ConversionException &) {
474 }
475 diag.emplace_back(
476 DiagLevel::Critical, argsToString("Unable to parse a valid date from the 'Year' frame and related frames: ", e.what()), diagContext);
477 }
478}
479
487void Id3v2Tag::parse(istream &stream, const std::uint64_t maximalSize, Diagnostics &diag)
488{
489 // prepare parsing
490 static const string context("parsing ID3v2 tag");
491 BinaryReader reader(&stream);
492 const auto startOffset = static_cast<std::uint64_t>(stream.tellg());
493
494 // check whether the header is truncated
495 if (maximalSize && maximalSize < 10) {
496 diag.emplace_back(DiagLevel::Critical, "ID3v2 header is truncated (at least 10 bytes expected).", context);
498 }
499
500 // read signature: ID3
501 if (reader.readUInt24BE() != 0x494433u) {
502 diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
503 throw InvalidDataException();
504 }
505 // read header data
506 const std::uint8_t majorVersion = reader.readByte();
507 const std::uint8_t revisionVersion = reader.readByte();
509 m_flags = reader.readByte();
510 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
511 m_size = 10 + m_sizeExcludingHeader;
512 if (m_sizeExcludingHeader == 0) {
513 diag.emplace_back(DiagLevel::Warning, "ID3v2 tag seems to be empty.", context);
514 return;
515 }
516
517 // check if the version
518 if (!isVersionSupported()) {
519 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
521 }
522
523 // read extended header (if present)
524 if (hasExtendedHeader()) {
525 if (maximalSize && maximalSize < 14) {
526 diag.emplace_back(DiagLevel::Critical, "Extended header denoted but not present.", context);
528 }
529 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
530 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
531 diag.emplace_back(DiagLevel::Critical, "Extended header is invalid/truncated.", context);
533 }
534 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
535 }
536
537 // how many bytes remain for frames and padding?
538 std::uint32_t bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
539 if (maximalSize && bytesRemaining > maximalSize) {
540 bytesRemaining = static_cast<std::uint32_t>(maximalSize);
541 diag.emplace_back(DiagLevel::Critical, "Frames are truncated.", context);
542 }
543
544 // read frames
545 auto pos = static_cast<std::uint64_t>(stream.tellg());
546 while (bytesRemaining) {
547 // seek to next frame
548 stream.seekg(static_cast<streamoff>(pos));
549 // parse frame
550 Id3v2Frame frame;
551 try {
552 frame.parse(reader, majorVersion, bytesRemaining, diag);
553 if (Id3v2FrameIds::isTextFrame(frame.id()) && fields().count(frame.id()) == 1) {
554 diag.emplace_back(DiagLevel::Warning, "The text frame " % frame.idToString() + " exists more than once.", context);
555 }
556 fields().emplace(frame.id(), std::move(frame));
557 } catch (const NoDataFoundException &) {
558 if (frame.hasPaddingReached()) {
559 m_paddingSize = startOffset + m_size - pos;
560 break;
561 }
562 } catch (const Failure &) {
563 }
564
565 // calculate next frame offset
566 if (frame.totalSize() <= bytesRemaining) {
567 pos += frame.totalSize();
568 bytesRemaining -= frame.totalSize();
569 } else {
570 pos += bytesRemaining;
571 bytesRemaining = 0;
572 }
573 }
574
575 if (m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
576 convertOldRecordDateFields(context, diag);
577 }
578
579 // check for extended header
580 if (!hasFooter()) {
581 return;
582 }
583 if (maximalSize && m_size + 10 < maximalSize) {
584 // the footer does not provide additional information, just check the signature
585 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
586 if (reader.readUInt24LE() != 0x494433u) {
587 diag.emplace_back(DiagLevel::Critical, "Footer signature is invalid.", context);
588 }
589 // skip remaining footer
590 stream.seekg(7, ios_base::cur);
591 } else {
592 diag.emplace_back(DiagLevel::Critical, "Footer denoted but not present.", context);
594 }
595}
596
608{
609 return Id3v2TagMaker(*this, diag);
610}
611
619void Id3v2Tag::make(ostream &stream, std::uint32_t padding, Diagnostics &diag)
620{
621 prepareMaking(diag).make(stream, padding, diag);
622}
623
628void Id3v2Tag::setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
629{
630 m_majorVersion = majorVersion;
631 m_revisionVersion = revisionVersion;
632 m_version = argsToString('2', '.', majorVersion, '.', revisionVersion);
633}
634
647bool FrameComparer::operator()(std::uint32_t lhs, std::uint32_t rhs) const
648{
649 if (lhs == rhs) {
650 return false;
651 }
652
653 const bool lhsLong = Id3v2FrameIds::isLongId(lhs);
654 const bool rhsLong = Id3v2FrameIds::isLongId(rhs);
655 if (lhsLong != rhsLong) {
656 if (!lhsLong) {
658 if (!lhs) {
659 return true;
660 }
661 } else if (!rhsLong) {
663 if (!rhs) {
664 return true;
665 }
666 }
667 }
668
670 return true;
671 }
673 return false;
674 }
675 if (lhs == Id3v2FrameIds::lTitle || lhs == Id3v2FrameIds::sTitle) {
676 return true;
677 }
678 if (rhs == Id3v2FrameIds::lTitle || rhs == Id3v2FrameIds::sTitle) {
679 return false;
680 }
681
682 const bool lhstextfield = Id3v2FrameIds::isTextFrame(lhs);
683 const bool rhstextfield = Id3v2FrameIds::isTextFrame(rhs);
684 if (lhstextfield && !rhstextfield) {
685 return true;
686 }
687 if (!lhstextfield && rhstextfield) {
688 return false;
689 }
690
691 if (lhs == Id3v2FrameIds::lCover || lhs == Id3v2FrameIds::sCover) {
692 return false;
693 }
694 if (rhs == Id3v2FrameIds::lCover || rhs == Id3v2FrameIds::sCover) {
695 return true;
696 }
697 return lhs < rhs;
698}
699
710void Id3v2Tag::removeOldRecordDateRelatedFields()
711{
713 fields().erase(field);
714 }
715}
716
720void Id3v2Tag::prepareRecordDataForMaking(const std::string &diagContext, Diagnostics &diag)
721{
722 // get rid of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime if writing v2.4.0 or newer
723 // note: If the tag was initially v2.3.0 or older the "old" fields have already been converted to lRecordingTime when
724 // parsing and the generic accessors propose using lRecordingTime in any case.
725 if (majorVersion() >= 4) {
726 removeOldRecordDateRelatedFields();
727 return;
728 }
729
730 // convert lRecordingTime to old fields for v2.3.0 and older
731 const auto recordingTimeFieldIterator = fields().find(Id3v2FrameIds::lRecordingTime);
732 // -> If the auto-created lRecordingTime field (see note above) has been completely removed write the old fields as-is.
733 // This allows one to bypass this handling and set the old fields explicitly.
734 if (recordingTimeFieldIterator == fields().cend()) {
735 return;
736 }
737 // -> simply remove all old fields if lRecordingTime is set to an empty value
738 const auto &recordingTime = recordingTimeFieldIterator->second.value();
739 if (recordingTime.isEmpty()) {
740 removeOldRecordDateRelatedFields();
741 return;
742 }
743 // -> convert lRecordingTime (which is supposed to be an ISO string) to a DateTime
744 try {
745 const auto dateTimeExpr = recordingTime.toDateTimeExpression();
746 const auto &asDateTime = dateTimeExpr.value;
747 // -> remove any existing old fields to avoid any leftovers
748 removeOldRecordDateRelatedFields();
749 // -> assign old fields from parsed DateTime
750 std::stringstream year, date, time;
751 if (dateTimeExpr.parts & DateTimeParts::Year) {
752 year << std::setfill('0') << std::setw(4) << asDateTime.year();
753 setValue(Id3v2FrameIds::lYear, TagValue(year.str()));
754 }
755 if (dateTimeExpr.parts & (DateTimeParts::Day | DateTimeParts::Month)) {
756 date << std::setfill('0') << std::setw(2) << asDateTime.day() << std::setfill('0') << std::setw(2) << asDateTime.month();
757 setValue(Id3v2FrameIds::lDate, TagValue(date.str()));
758 }
759 if (dateTimeExpr.parts & DateTimeParts::Time) {
760 time << std::setfill('0') << std::setw(2) << asDateTime.hour() << std::setfill('0') << std::setw(2) << asDateTime.minute();
761 setValue(Id3v2FrameIds::lTime, TagValue(time.str()));
762 }
763 if (dateTimeExpr.parts & (DateTimeParts::Second | DateTimeParts::SubSecond)) {
764 diag.emplace_back(DiagLevel::Warning,
765 "The recording time field (TDRC) has been truncated to full minutes when converting to corresponding fields for older ID3v2 "
766 "versions.",
767 diagContext);
768 }
769 } catch (const ConversionException &e) {
770 try {
771 diag.emplace_back(DiagLevel::Critical,
772 argsToString("Unable to convert recording time field (TDRC) with the value \"", recordingTime.toString(),
773 "\" to corresponding fields for older ID3v2 versions: ", e.what()),
774 diagContext);
775 } catch (const ConversionException &) {
776 diag.emplace_back(DiagLevel::Critical,
777 argsToString("Unable to convert recording time field (TRDA) to corresponding fields for older ID3v2 versions: ", e.what()),
778 diagContext);
779 }
780 }
781 // -> get rid of lRecordingTime
783}
784
789Id3v2TagMaker::Id3v2TagMaker(Id3v2Tag &tag, Diagnostics &diag)
790 : m_tag(tag)
791 , m_framesSize(0)
792{
793 static const string context("making ID3v2 tag");
794
795 // check if version is supported
796 // (the version could have been changed using setVersion())
797 if (!tag.isVersionSupported()) {
798 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag version isn't supported.", context);
799 throw VersionNotSupportedException();
800 }
801
802 if (m_tag.m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
803 tag.prepareRecordDataForMaking(context, diag);
804 }
805
806 // prepare frames
807 m_maker.reserve(tag.fields().size());
808 for (auto &pair : tag.fields()) {
809 try {
810 m_maker.emplace_back(pair.second.prepareMaking(tag.majorVersion(), diag));
811 m_framesSize += m_maker.back().requiredSize();
812 } catch (const Failure &) {
813 }
814 }
815
816 // calculate required size
817 // -> header + size of frames
818 m_requiredSize = 10 + m_framesSize;
819}
820
828void Id3v2TagMaker::make(std::ostream &stream, std::uint32_t padding, Diagnostics &diag)
829{
830 CPP_UTILITIES_UNUSED(diag)
831
832 BinaryWriter writer(&stream);
833
834 // write header
835 // -> signature
836 writer.writeUInt24BE(0x494433u);
837 // -> version
838 writer.writeByte(m_tag.majorVersion());
839 writer.writeByte(m_tag.revisionVersion());
840 // -> flags, but without extended header or compression bit set
841 writer.writeByte(m_tag.flags() & 0xBF);
842 // -> size (excluding header)
843 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
844
845 // write frames
846 for (auto &maker : m_maker) {
847 maker.make(writer);
848 }
849
850 MediaFileInfo::writePadding(stream, padding);
851}
852
853} // 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...
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
The Id3v2Frame class is used by Id3v2Tag to store the fields.
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
The Id3v2TagMaker class helps writing ID3v2 tags.
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:828
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:619
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:27
void setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
Sets the version to the specified majorVersion and the specified revisionVersion.
Definition id3v2tag.cpp:628
KnownField internallyGetKnownField(const IdentifierType &id) const
Definition id3v2tag.cpp:258
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:73
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:91
TagTextEncoding proposedTextEncoding() const override
Returns the proposed text encoding.
Definition id3v2tag.h:149
TagDataType internallyGetProposedDataType(const std::uint32_t &id) const
Definition id3v2tag.cpp:372
Id3v2TagMaker prepareMaking(Diagnostics &diag)
Prepares making.
Definition id3v2tag.cpp:607
void ensureTextValuesAreProperlyEncoded() override
Ensures the encoding of all assigned text values is supported by the tag by converting the character ...
Definition id3v2tag.cpp:60
IdentifierType internallyGetFieldId(KnownField field) const
Definition id3v2tag.cpp:133
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:487
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
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....
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.
void convertDataEncoding(TagTextEncoding encoding)
Converts the currently assigned text value to the specified encoding.
Definition tagvalue.cpp:921
bool isEmpty() const
Returns whether no or an empty value is assigned.
Definition tagvalue.h:490
void convertDescriptionEncoding(TagTextEncoding encoding)
Converts the assigned description to use the specified encoding.
Definition tagvalue.cpp:973
std::string m_version
Definition tag.h:205
std::uint64_t m_size
Definition tag.h:206
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
The exception that is thrown when an operation fails because the detected or specified version is not...
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:29
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:647