Tag Parser 11.2.1
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;
184 return lRemixedBy;
186 return lCopyright;
188 return lTaggingTime;
190 return lEncodingTime;
193 case KnownField::Mood:
194 return lMood;
195 default:;
196 }
197 } else {
198 switch (field) {
200 return sAlbum;
202 return sArtist;
204 return sComment;
206 return lRecordingTime; // (de)serializer takes to convert to/from sYear/sRecordingDates/sDate/sTime
208 return sTitle;
210 return sGenre;
212 return sTrackPosition;
214 return sDiskPosition;
216 return sEncoder;
217 case KnownField::Bpm:
218 return sBpm;
220 return sCover;
222 return sWriter;
224 return sLength;
226 return sLanguage;
228 return sEncoderSettings;
232 return sSynchronizedLyrics;
236 return sRecordLabel;
238 return sComposer;
240 return sRating;
242 return sAlbumArtist;
244 return sRemixedBy;
246 return sCopyright;
247 default:;
248 }
249 }
250 return 0;
251}
252
253KnownField Id3v2Tag::internallyGetKnownField(const IdentifierType &id) const
254{
255 using namespace Id3v2FrameIds;
256 switch (id) {
257 case lAlbum:
258 return KnownField::Album;
259 case lArtist:
260 return KnownField::Artist;
261 case lComment:
262 return KnownField::Comment;
263 case lRecordingTime:
264 case lYear:
266 case lTitle:
267 return KnownField::Title;
268 case lGenre:
269 return KnownField::Genre;
270 case lTrackPosition:
272 case lDiskPosition:
274 case lEncoder:
275 return KnownField::Encoder;
276 case lBpm:
277 return KnownField::Bpm;
278 case lCover:
279 return KnownField::Cover;
280 case lWriter:
282 case lLanguage:
284 case lLength:
285 return KnownField::Length;
286 case lEncoderSettings:
289 return KnownField::Lyrics;
292 case lAlbumArtist:
294 case lRemixedBy:
296 case lCopyright:
300 case lRecordLabel:
302 case lTaggingTime:
304 case lEncodingTime:
308 case lMood:
309 return KnownField::Mood;
310 case lISRC:
311 return KnownField::ISRC;
312 case sAlbum:
313 return KnownField::Album;
314 case sArtist:
315 return KnownField::Artist;
316 case sComment:
317 return KnownField::Comment;
318 case sYear:
320 case sTitle:
321 return KnownField::Title;
322 case sGenre:
323 return KnownField::Genre;
324 case sTrackPosition:
326 case sEncoder:
327 return KnownField::Encoder;
328 case sBpm:
329 return KnownField::Bpm;
330 case sCover:
331 return KnownField::Cover;
332 case sWriter:
334 case sLanguage:
336 case sLength:
337 return KnownField::Length;
338 case sEncoderSettings:
341 return KnownField::Lyrics;
344 case sAlbumArtist:
346 case sRecordLabel:
348 case sRemixedBy:
350 case sCopyright:
352 case sISRC:
353 return KnownField::ISRC;
354 default:
355 return KnownField::Invalid;
356 }
357}
358
360{
361 using namespace Id3v2FrameIds;
362 switch (id) {
363 case lLength:
364 case sLength:
366 case lBpm:
367 case sBpm:
368 case lYear:
369 case sYear:
371 case lTrackPosition:
372 case sTrackPosition:
373 case lDiskPosition:
375 case lCover:
376 case sCover:
378 default:
380 return TagDataType::Text;
381 } else {
383 }
384 }
385}
386
393void Id3v2Tag::convertOldRecordDateFields(const std::string &diagContext, Diagnostics &diag)
394{
395 // skip if it is a v2.4.0 tag and lRecordingTime is present
396 if (majorVersion() >= 4 && fields().find(Id3v2FrameIds::lRecordingTime) != fields().cend()) {
397 return;
398 }
399
400 // parse values of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime fields
401 bool hasAnyValue = false;
402 int year = 1, month = 1, day = 1, hour = 0, minute = 0;
403 if (const auto &v = value(Id3v2FrameIds::lYear)) {
404 hasAnyValue = true;
405 try {
406 year = v.toInteger();
407 } catch (const ConversionException &e) {
408 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse year from \"TYER\" frame: ", e.what()), diagContext);
409 }
410 }
411 if (const auto &v = value(Id3v2FrameIds::lDate)) {
412 hasAnyValue = true;
413 try {
414 auto str = v.toString();
415 if (str.size() != 4) {
416 throw ConversionException("format is not DDMM");
417 }
418 day = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
419 month = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
420 } catch (const ConversionException &e) {
421 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse month and day from \"TDAT\" frame: ", e.what()), diagContext);
422 }
423 }
424 if (const auto &v = value(Id3v2FrameIds::lTime)) {
425 hasAnyValue = true;
426 try {
427 auto str = v.toString();
428 if (str.size() != 4) {
429 throw ConversionException("format is not HHMM");
430 }
431 hour = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
432 minute = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
433 } catch (const ConversionException &e) {
434 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse hour and minute from \"TIME\" frame: ", +e.what()), diagContext);
435 }
436 }
437
438 // set the field values as DateTime
439 if (!hasAnyValue) {
440 return;
441 }
442 try {
443 setValue(Id3v2FrameIds::lRecordingTime, TagValue(DateTime::fromDateAndTime(year, month, day, hour, minute)));
444 } catch (const ConversionException &e) {
445 try {
446 // try to set at least the year
447 setValue(Id3v2FrameIds::lRecordingTime, TagValue(DateTime::fromDate(year)));
448 diag.emplace_back(DiagLevel::Critical,
449 argsToString(
450 "Unable to parse the full date of the recording. Only the 'Year' frame could be parsed; related frames failed: ", e.what()),
451 diagContext);
452 } catch (const ConversionException &) {
453 }
454 diag.emplace_back(
455 DiagLevel::Critical, argsToString("Unable to parse a valid date from the 'Year' frame and related frames: ", e.what()), diagContext);
456 }
457}
458
466void Id3v2Tag::parse(istream &stream, const std::uint64_t maximalSize, Diagnostics &diag)
467{
468 // prepare parsing
469 static const string context("parsing ID3v2 tag");
470 BinaryReader reader(&stream);
471 const auto startOffset = static_cast<std::uint64_t>(stream.tellg());
472
473 // check whether the header is truncated
474 if (maximalSize && maximalSize < 10) {
475 diag.emplace_back(DiagLevel::Critical, "ID3v2 header is truncated (at least 10 bytes expected).", context);
477 }
478
479 // read signature: ID3
480 if (reader.readUInt24BE() != 0x494433u) {
481 diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
482 throw InvalidDataException();
483 }
484 // read header data
485 const std::uint8_t majorVersion = reader.readByte();
486 const std::uint8_t revisionVersion = reader.readByte();
488 m_flags = reader.readByte();
489 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
490 m_size = 10 + m_sizeExcludingHeader;
491 if (m_sizeExcludingHeader == 0) {
492 diag.emplace_back(DiagLevel::Warning, "ID3v2 tag seems to be empty.", context);
493 return;
494 }
495
496 // check if the version
497 if (!isVersionSupported()) {
498 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
500 }
501
502 // read extended header (if present)
503 if (hasExtendedHeader()) {
504 if (maximalSize && maximalSize < 14) {
505 diag.emplace_back(DiagLevel::Critical, "Extended header denoted but not present.", context);
507 }
508 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
509 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
510 diag.emplace_back(DiagLevel::Critical, "Extended header is invalid/truncated.", context);
512 }
513 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
514 }
515
516 // how many bytes remain for frames and padding?
517 std::uint32_t bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
518 if (maximalSize && bytesRemaining > maximalSize) {
519 bytesRemaining = static_cast<std::uint32_t>(maximalSize);
520 diag.emplace_back(DiagLevel::Critical, "Frames are truncated.", context);
521 }
522
523 // read frames
524 auto pos = static_cast<std::uint64_t>(stream.tellg());
525 while (bytesRemaining) {
526 // seek to next frame
527 stream.seekg(static_cast<streamoff>(pos));
528 // parse frame
529 Id3v2Frame frame;
530 try {
531 frame.parse(reader, majorVersion, bytesRemaining, diag);
532 if (Id3v2FrameIds::isTextFrame(frame.id()) && fields().count(frame.id()) == 1) {
533 diag.emplace_back(DiagLevel::Warning, "The text frame " % frame.idToString() + " exists more than once.", context);
534 }
535 fields().emplace(frame.id(), move(frame));
536 } catch (const NoDataFoundException &) {
537 if (frame.hasPaddingReached()) {
538 m_paddingSize = startOffset + m_size - pos;
539 break;
540 }
541 } catch (const Failure &) {
542 }
543
544 // calculate next frame offset
545 if (frame.totalSize() <= bytesRemaining) {
546 pos += frame.totalSize();
547 bytesRemaining -= frame.totalSize();
548 } else {
549 pos += bytesRemaining;
550 bytesRemaining = 0;
551 }
552 }
553
554 if (m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
555 convertOldRecordDateFields(context, diag);
556 }
557
558 // check for extended header
559 if (!hasFooter()) {
560 return;
561 }
562 if (maximalSize && m_size + 10 < maximalSize) {
563 // the footer does not provide additional information, just check the signature
564 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
565 if (reader.readUInt24LE() != 0x494433u) {
566 diag.emplace_back(DiagLevel::Critical, "Footer signature is invalid.", context);
567 }
568 // skip remaining footer
569 stream.seekg(7, ios_base::cur);
570 } else {
571 diag.emplace_back(DiagLevel::Critical, "Footer denoted but not present.", context);
573 }
574}
575
587{
588 return Id3v2TagMaker(*this, diag);
589}
590
598void Id3v2Tag::make(ostream &stream, std::uint32_t padding, Diagnostics &diag)
599{
600 prepareMaking(diag).make(stream, padding, diag);
601}
602
607void Id3v2Tag::setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
608{
609 m_majorVersion = majorVersion;
610 m_revisionVersion = revisionVersion;
611 m_version = argsToString('2', '.', majorVersion, '.', revisionVersion);
612}
613
626bool FrameComparer::operator()(std::uint32_t lhs, std::uint32_t rhs) const
627{
628 if (lhs == rhs) {
629 return false;
630 }
631
632 const bool lhsLong = Id3v2FrameIds::isLongId(lhs);
633 const bool rhsLong = Id3v2FrameIds::isLongId(rhs);
634 if (lhsLong != rhsLong) {
635 if (!lhsLong) {
637 if (!lhs) {
638 return true;
639 }
640 } else if (!rhsLong) {
642 if (!rhs) {
643 return true;
644 }
645 }
646 }
647
649 return true;
650 }
652 return false;
653 }
654 if (lhs == Id3v2FrameIds::lTitle || lhs == Id3v2FrameIds::sTitle) {
655 return true;
656 }
657 if (rhs == Id3v2FrameIds::lTitle || rhs == Id3v2FrameIds::sTitle) {
658 return false;
659 }
660
661 const bool lhstextfield = Id3v2FrameIds::isTextFrame(lhs);
662 const bool rhstextfield = Id3v2FrameIds::isTextFrame(rhs);
663 if (lhstextfield && !rhstextfield) {
664 return true;
665 }
666 if (!lhstextfield && rhstextfield) {
667 return false;
668 }
669
670 if (lhs == Id3v2FrameIds::lCover || lhs == Id3v2FrameIds::sCover) {
671 return false;
672 }
673 if (rhs == Id3v2FrameIds::lCover || rhs == Id3v2FrameIds::sCover) {
674 return true;
675 }
676 return lhs < rhs;
677}
678
689void Id3v2Tag::removeOldRecordDateRelatedFields()
690{
692 fields().erase(field);
693 }
694}
695
699void Id3v2Tag::prepareRecordDataForMaking(const std::string &diagContext, Diagnostics &diag)
700{
701 // get rid of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime if writing v2.4.0 or newer
702 // note: If the tag was initially v2.3.0 or older the "old" fields have already been converted to lRecordingTime when
703 // parsing and the generic accessors propose using lRecordingTime in any case.
704 if (majorVersion() >= 4) {
705 removeOldRecordDateRelatedFields();
706 return;
707 }
708
709 // convert lRecordingTime to old fields for v2.3.0 and older
710 const auto recordingTimeFieldIterator = fields().find(Id3v2FrameIds::lRecordingTime);
711 // -> If the auto-created lRecordingTime field (see note above) has been completely removed write the old fields as-is.
712 // This allows one to bypass this handling and set the old fields explicitly.
713 if (recordingTimeFieldIterator == fields().cend()) {
714 return;
715 }
716 // -> simply remove all old fields if lRecordingTime is set to an empty value
717 const auto &recordingTime = recordingTimeFieldIterator->second.value();
718 if (recordingTime.isEmpty()) {
719 removeOldRecordDateRelatedFields();
720 return;
721 }
722 // -> convert lRecordingTime (which is supposed to be an ISO string) to a DateTime
723 try {
724 const auto asDateTime = recordingTime.toDateTime();
725 // -> remove any existing old fields to avoid any leftovers
726 removeOldRecordDateRelatedFields();
727 // -> assign old fields from parsed DateTime
728 std::stringstream year, date, time;
729 year << std::setfill('0') << std::setw(4) << asDateTime.year();
730 setValue(Id3v2FrameIds::lYear, TagValue(year.str()));
731 date << std::setfill('0') << std::setw(2) << asDateTime.day() << std::setfill('0') << std::setw(2) << asDateTime.month();
732 setValue(Id3v2FrameIds::lDate, TagValue(date.str()));
733 time << std::setfill('0') << std::setw(2) << asDateTime.hour() << std::setfill('0') << std::setw(2) << asDateTime.minute();
734 setValue(Id3v2FrameIds::lTime, TagValue(time.str()));
735 if (asDateTime.second() || asDateTime.millisecond()) {
736 diag.emplace_back(DiagLevel::Warning,
737 "The recording time field (TRDA) has been truncated to full minutes when converting to corresponding fields for older ID3v2 "
738 "versions.",
739 diagContext);
740 }
741 } catch (const ConversionException &e) {
742 try {
743 diag.emplace_back(DiagLevel::Critical,
744 argsToString("Unable to convert recording time field (TRDA) with the value \"", recordingTime.toString(),
745 "\" to corresponding fields for older ID3v2 versions: ", e.what()),
746 diagContext);
747 } catch (const ConversionException &) {
748 diag.emplace_back(DiagLevel::Critical,
749 argsToString("Unable to convert recording time field (TRDA) to corresponding fields for older ID3v2 versions: ", e.what()),
750 diagContext);
751 }
752 }
753 // -> get rid of lRecordingTime
755}
756
761Id3v2TagMaker::Id3v2TagMaker(Id3v2Tag &tag, Diagnostics &diag)
762 : m_tag(tag)
763 , m_framesSize(0)
764{
765 static const string context("making ID3v2 tag");
766
767 // check if version is supported
768 // (the version could have been changed using setVersion())
769 if (!tag.isVersionSupported()) {
770 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag version isn't supported.", context);
771 throw VersionNotSupportedException();
772 }
773
774 if (m_tag.m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
775 tag.prepareRecordDataForMaking(context, diag);
776 }
777
778 // prepare frames
779 m_maker.reserve(tag.fields().size());
780 for (auto &pair : tag.fields()) {
781 try {
782 m_maker.emplace_back(pair.second.prepareMaking(tag.majorVersion(), diag));
783 m_framesSize += m_maker.back().requiredSize();
784 } catch (const Failure &) {
785 }
786 }
787
788 // calculate required size
789 // -> header + size of frames
790 m_requiredSize = 10 + m_framesSize;
791}
792
800void Id3v2TagMaker::make(std::ostream &stream, std::uint32_t padding, Diagnostics &diag)
801{
802 CPP_UTILITIES_UNUSED(diag)
803
804 BinaryWriter writer(&stream);
805
806 // write header
807 // -> signature
808 writer.writeUInt24BE(0x494433u);
809 // -> version
810 writer.writeByte(m_tag.majorVersion());
811 writer.writeByte(m_tag.revisionVersion());
812 // -> flags, but without extended header or compression bit set
813 writer.writeByte(m_tag.flags() & 0xBF);
814 // -> size (excluding header)
815 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
816
817 // write frames
818 for (auto &maker : m_maker) {
819 maker.make(writer);
820 }
821
822 // write padding
823 for (; padding; --padding) {
824 stream.put(0);
825 }
826}
827
828} // 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:800
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:598
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:607
KnownField internallyGetKnownField(const IdentifierType &id) const
Definition: id3v2tag.cpp:253
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:359
Id3v2TagMaker prepareMaking(Diagnostics &diag)
Prepares making.
Definition: id3v2tag.cpp:586
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:466
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:214
std::uint64_t m_size
Definition: tag.h:215
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:93
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:626