Tag Parser 11.1.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
id3v2tag.cpp
Go to the documentation of this file.
1#include "./id3v2tag.h"
2#include "./id3v2frameids.h"
3
4#include "../diagnostics.h"
5#include "../exceptions.h"
6
7#include <c++utilities/conversion/stringbuilder.h>
8#include <c++utilities/conversion/stringconversion.h>
9
10#include <iostream>
11
12using namespace std;
13using namespace CppUtilities;
14
15namespace TagParser {
16
27{
28 switch (field) {
38 case KnownField::Bpm:
47 return m_majorVersion > 3;
53 return true;
54 default:
55 return false;
56 }
57}
58
60{
61 const auto encoding = proposedTextEncoding();
62 for (auto &field : fields()) {
63 auto &value = field.second.value();
64 value.convertDataEncoding(encoding);
66 }
67}
68
72void Id3v2Tag::internallyGetValuesFromField(const Id3v2Tag::FieldType &field, std::vector<const TagValue *> &values) const
73{
74 if (!field.value().isEmpty()) {
75 values.emplace_back(&field.value());
76 }
77 for (const auto &value : field.additionalValues()) {
78 if (!value.isEmpty()) {
79 values.emplace_back(&value);
80 }
81 }
82}
83
90bool Id3v2Tag::internallySetValues(const IdentifierType &id, const std::vector<TagValue> &values)
91{
92 // use default implementation for non-text frames
95 }
96
97 // find existing text frame
98 auto range = fields().equal_range(id);
99 auto frameIterator = range.first;
100
101 // use existing frame or insert new text frame
102 auto valuesIterator = values.cbegin();
103 if (frameIterator != range.second) {
104 ++range.first;
105 // add primary value to existing frame
106 if (valuesIterator != values.cend()) {
107 frameIterator->second.setValue(*valuesIterator);
108 ++valuesIterator;
109 } else {
110 frameIterator->second.value().clearDataAndMetadata();
111 }
112 } else {
113 // skip if there is no existing frame but also no values to be assigned
114 if (valuesIterator == values.cend()) {
115 return true;
116 }
117 // add primary value to new frame
118 frameIterator = fields().insert(make_pair(id, Id3v2Frame(id, *valuesIterator)));
119 ++valuesIterator;
120 }
121
122 // add additional values to frame
123 frameIterator->second.additionalValues() = vector<TagValue>(valuesIterator, values.cend());
124
125 // remove remaining existing values (there are more existing values than specified ones)
126 for (; range.first != range.second; ++range.first) {
127 range.first->second.setValue(TagValue());
128 }
129 return true;
130}
131
133{
134 using namespace Id3v2FrameIds;
135 if (m_majorVersion >= 3) {
136 switch (field) {
138 return lAlbum;
140 return lArtist;
142 return lComment;
144 return lRecordingTime; // (de)serializer takes to convert to/from lYear/lRecordingDates/lDate/lTime
146 return lReleaseTime;
148 return lTitle;
150 return lGenre;
152 return lTrackPosition;
154 return lDiskPosition;
156 return lEncoder;
157 case KnownField::Bpm:
158 return lBpm;
160 return lCover;
162 return lWriter;
164 return lLength;
166 return lLanguage;
168 return lEncoderSettings;
172 return lSynchronizedLyrics;
176 return lRecordLabel;
178 return lComposer;
180 return lRating;
182 return lAlbumArtist;
184 return lRemixedBy;
186 return lCopyright;
188 return lTaggingTime;
190 return lEncodingTime;
193 default:;
194 }
195 } else {
196 switch (field) {
198 return sAlbum;
200 return sArtist;
202 return sComment;
204 return lRecordingTime; // (de)serializer takes to convert to/from sYear/sRecordingDates/sDate/sTime
206 return sTitle;
208 return sGenre;
210 return sTrackPosition;
212 return sDiskPosition;
214 return sEncoder;
215 case KnownField::Bpm:
216 return sBpm;
218 return sCover;
220 return sWriter;
222 return sLength;
224 return sLanguage;
226 return sEncoderSettings;
230 return sSynchronizedLyrics;
234 return sRecordLabel;
236 return sComposer;
238 return sRating;
240 return sAlbumArtist;
242 return sRemixedBy;
244 return sCopyright;
245 default:;
246 }
247 }
248 return 0;
249}
250
251KnownField Id3v2Tag::internallyGetKnownField(const IdentifierType &id) const
252{
253 using namespace Id3v2FrameIds;
254 switch (id) {
255 case lAlbum:
256 return KnownField::Album;
257 case lArtist:
258 return KnownField::Artist;
259 case lComment:
260 return KnownField::Comment;
261 case lRecordingTime:
262 case lYear:
264 case lTitle:
265 return KnownField::Title;
266 case lGenre:
267 return KnownField::Genre;
268 case lTrackPosition:
270 case lDiskPosition:
272 case lEncoder:
273 return KnownField::Encoder;
274 case lBpm:
275 return KnownField::Bpm;
276 case lCover:
277 return KnownField::Cover;
278 case lWriter:
280 case lLanguage:
282 case lLength:
283 return KnownField::Length;
284 case lEncoderSettings:
287 return KnownField::Lyrics;
290 case lAlbumArtist:
292 case lRemixedBy:
294 case lCopyright:
298 case lRecordLabel:
300 case lTaggingTime:
302 case lEncodingTime:
306 case sAlbum:
307 return KnownField::Album;
308 case sArtist:
309 return KnownField::Artist;
310 case sComment:
311 return KnownField::Comment;
312 case sYear:
314 case sTitle:
315 return KnownField::Title;
316 case sGenre:
317 return KnownField::Genre;
318 case sTrackPosition:
320 case sEncoder:
321 return KnownField::Encoder;
322 case sBpm:
323 return KnownField::Bpm;
324 case sCover:
325 return KnownField::Cover;
326 case sWriter:
328 case sLanguage:
330 case sLength:
331 return KnownField::Length;
332 case sEncoderSettings:
335 return KnownField::Lyrics;
338 case sAlbumArtist:
340 case sRecordLabel:
342 case sRemixedBy:
344 case sCopyright:
346 default:
347 return KnownField::Invalid;
348 }
349}
350
352{
353 using namespace Id3v2FrameIds;
354 switch (id) {
355 case lLength:
356 case sLength:
358 case lBpm:
359 case sBpm:
360 case lYear:
361 case sYear:
363 case lTrackPosition:
364 case sTrackPosition:
365 case lDiskPosition:
367 case lCover:
368 case sCover:
370 default:
372 return TagDataType::Text;
373 } else {
375 }
376 }
377}
378
385void Id3v2Tag::convertOldRecordDateFields(const std::string &diagContext, Diagnostics &diag)
386{
387 // skip if it is a v2.4.0 tag and lRecordingTime is present
388 if (majorVersion() >= 4 && fields().find(Id3v2FrameIds::lRecordingTime) != fields().cend()) {
389 return;
390 }
391
392 // parse values of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime fields
393 bool hasAnyValue = false;
394 int year = 1, month = 1, day = 1, hour = 0, minute = 0;
395 if (const auto &v = value(Id3v2FrameIds::lYear)) {
396 hasAnyValue = true;
397 try {
398 year = v.toInteger();
399 } catch (const ConversionException &e) {
400 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse year from \"TYER\" frame: ", e.what()), diagContext);
401 }
402 }
403 if (const auto &v = value(Id3v2FrameIds::lDate)) {
404 hasAnyValue = true;
405 try {
406 auto str = v.toString();
407 if (str.size() != 4) {
408 throw ConversionException("format is not DDMM");
409 }
410 day = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
411 month = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
412 } catch (const ConversionException &e) {
413 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse month and day from \"TDAT\" frame: ", e.what()), diagContext);
414 }
415 }
416 if (const auto &v = value(Id3v2FrameIds::lTime)) {
417 hasAnyValue = true;
418 try {
419 auto str = v.toString();
420 if (str.size() != 4) {
421 throw ConversionException("format is not HHMM");
422 }
423 hour = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
424 minute = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
425 } catch (const ConversionException &e) {
426 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse hour and minute from \"TIME\" frame: ", +e.what()), diagContext);
427 }
428 }
429
430 // set the field values as DateTime
431 if (!hasAnyValue) {
432 return;
433 }
434 try {
435 setValue(Id3v2FrameIds::lRecordingTime, TagValue(DateTime::fromDateAndTime(year, month, day, hour, minute)));
436 } catch (const ConversionException &e) {
437 try {
438 // try to set at least the year
439 setValue(Id3v2FrameIds::lRecordingTime, TagValue(DateTime::fromDate(year)));
440 diag.emplace_back(DiagLevel::Critical,
441 argsToString(
442 "Unable to parse the full date of the recording. Only the 'Year' frame could be parsed; related frames failed: ", e.what()),
443 diagContext);
444 } catch (const ConversionException &) {
445 }
446 diag.emplace_back(
447 DiagLevel::Critical, argsToString("Unable to parse a valid date from the 'Year' frame and related frames: ", e.what()), diagContext);
448 }
449}
450
458void Id3v2Tag::parse(istream &stream, const std::uint64_t maximalSize, Diagnostics &diag)
459{
460 // prepare parsing
461 static const string context("parsing ID3v2 tag");
462 BinaryReader reader(&stream);
463 const auto startOffset = static_cast<std::uint64_t>(stream.tellg());
464
465 // check whether the header is truncated
466 if (maximalSize && maximalSize < 10) {
467 diag.emplace_back(DiagLevel::Critical, "ID3v2 header is truncated (at least 10 bytes expected).", context);
469 }
470
471 // read signature: ID3
472 if (reader.readUInt24BE() != 0x494433u) {
473 diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
474 throw InvalidDataException();
475 }
476 // read header data
477 const std::uint8_t majorVersion = reader.readByte();
478 const std::uint8_t revisionVersion = reader.readByte();
480 m_flags = reader.readByte();
481 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
482 m_size = 10 + m_sizeExcludingHeader;
483 if (m_sizeExcludingHeader == 0) {
484 diag.emplace_back(DiagLevel::Warning, "ID3v2 tag seems to be empty.", context);
485 return;
486 }
487
488 // check if the version
489 if (!isVersionSupported()) {
490 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
492 }
493
494 // read extended header (if present)
495 if (hasExtendedHeader()) {
496 if (maximalSize && maximalSize < 14) {
497 diag.emplace_back(DiagLevel::Critical, "Extended header denoted but not present.", context);
499 }
500 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
501 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
502 diag.emplace_back(DiagLevel::Critical, "Extended header is invalid/truncated.", context);
504 }
505 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
506 }
507
508 // how many bytes remain for frames and padding?
509 std::uint32_t bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
510 if (maximalSize && bytesRemaining > maximalSize) {
511 bytesRemaining = static_cast<std::uint32_t>(maximalSize);
512 diag.emplace_back(DiagLevel::Critical, "Frames are truncated.", context);
513 }
514
515 // read frames
516 auto pos = static_cast<std::uint64_t>(stream.tellg());
517 while (bytesRemaining) {
518 // seek to next frame
519 stream.seekg(static_cast<streamoff>(pos));
520 // parse frame
521 Id3v2Frame frame;
522 try {
523 frame.parse(reader, majorVersion, bytesRemaining, diag);
524 if (Id3v2FrameIds::isTextFrame(frame.id()) && fields().count(frame.id()) == 1) {
525 diag.emplace_back(DiagLevel::Warning, "The text frame " % frame.idToString() + " exists more than once.", context);
526 }
527 fields().emplace(frame.id(), move(frame));
528 } catch (const NoDataFoundException &) {
529 if (frame.hasPaddingReached()) {
530 m_paddingSize = startOffset + m_size - pos;
531 break;
532 }
533 } catch (const Failure &) {
534 }
535
536 // calculate next frame offset
537 if (frame.totalSize() <= bytesRemaining) {
538 pos += frame.totalSize();
539 bytesRemaining -= frame.totalSize();
540 } else {
541 pos += bytesRemaining;
542 bytesRemaining = 0;
543 }
544 }
545
546 if (m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
547 convertOldRecordDateFields(context, diag);
548 }
549
550 // check for extended header
551 if (!hasFooter()) {
552 return;
553 }
554 if (maximalSize && m_size + 10 < maximalSize) {
555 // the footer does not provide additional information, just check the signature
556 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
557 if (reader.readUInt24LE() != 0x494433u) {
558 diag.emplace_back(DiagLevel::Critical, "Footer signature is invalid.", context);
559 }
560 // skip remaining footer
561 stream.seekg(7, ios_base::cur);
562 } else {
563 diag.emplace_back(DiagLevel::Critical, "Footer denoted but not present.", context);
565 }
566}
567
579{
580 return Id3v2TagMaker(*this, diag);
581}
582
590void Id3v2Tag::make(ostream &stream, std::uint32_t padding, Diagnostics &diag)
591{
592 prepareMaking(diag).make(stream, padding, diag);
593}
594
599void Id3v2Tag::setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
600{
601 m_majorVersion = majorVersion;
602 m_revisionVersion = revisionVersion;
603 m_version = argsToString('2', '.', majorVersion, '.', revisionVersion);
604}
605
618bool FrameComparer::operator()(std::uint32_t lhs, std::uint32_t rhs) const
619{
620 if (lhs == rhs) {
621 return false;
622 }
623
624 const bool lhsLong = Id3v2FrameIds::isLongId(lhs);
625 const bool rhsLong = Id3v2FrameIds::isLongId(rhs);
626 if (lhsLong != rhsLong) {
627 if (!lhsLong) {
629 if (!lhs) {
630 return true;
631 }
632 } else if (!rhsLong) {
634 if (!rhs) {
635 return true;
636 }
637 }
638 }
639
641 return true;
642 }
644 return false;
645 }
646 if (lhs == Id3v2FrameIds::lTitle || lhs == Id3v2FrameIds::sTitle) {
647 return true;
648 }
649 if (rhs == Id3v2FrameIds::lTitle || rhs == Id3v2FrameIds::sTitle) {
650 return false;
651 }
652
653 const bool lhstextfield = Id3v2FrameIds::isTextFrame(lhs);
654 const bool rhstextfield = Id3v2FrameIds::isTextFrame(rhs);
655 if (lhstextfield && !rhstextfield) {
656 return true;
657 }
658 if (!lhstextfield && rhstextfield) {
659 return false;
660 }
661
662 if (lhs == Id3v2FrameIds::lCover || lhs == Id3v2FrameIds::sCover) {
663 return false;
664 }
665 if (rhs == Id3v2FrameIds::lCover || rhs == Id3v2FrameIds::sCover) {
666 return true;
667 }
668 return lhs < rhs;
669}
670
681void Id3v2Tag::removeOldRecordDateRelatedFields()
682{
684 fields().erase(field);
685 }
686}
687
691void Id3v2Tag::prepareRecordDataForMaking(const std::string &diagContext, Diagnostics &diag)
692{
693 // get rid of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime if writing v2.4.0 or newer
694 // note: If the tag was initially v2.3.0 or older the "old" fields have already been converted to lRecordingTime when
695 // parsing and the generic accessors propose using lRecordingTime in any case.
696 if (majorVersion() >= 4) {
697 removeOldRecordDateRelatedFields();
698 return;
699 }
700
701 // convert lRecordingTime to old fields for v2.3.0 and older
702 const auto recordingTimeFieldIterator = fields().find(Id3v2FrameIds::lRecordingTime);
703 // -> If the auto-created lRecordingTime field (see note above) has been completely removed write the old fields as-is.
704 // This allows one to bypass this handling and set the old fields explicitly.
705 if (recordingTimeFieldIterator == fields().cend()) {
706 return;
707 }
708 // -> simply remove all old fields if lRecordingTime is set to an empty value
709 const auto &recordingTime = recordingTimeFieldIterator->second.value();
710 if (recordingTime.isEmpty()) {
711 removeOldRecordDateRelatedFields();
712 return;
713 }
714 // -> convert lRecordingTime (which is supposed to be an ISO string) to a DateTime
715 try {
716 const auto asDateTime = recordingTime.toDateTime();
717 // -> remove any existing old fields to avoid any leftovers
718 removeOldRecordDateRelatedFields();
719 // -> assign old fields from parsed DateTime
720 std::stringstream year, date, time;
721 year << std::setfill('0') << std::setw(4) << asDateTime.year();
722 setValue(Id3v2FrameIds::lYear, TagValue(year.str()));
723 date << std::setfill('0') << std::setw(2) << asDateTime.day() << std::setfill('0') << std::setw(2) << asDateTime.month();
724 setValue(Id3v2FrameIds::lDate, TagValue(date.str()));
725 time << std::setfill('0') << std::setw(2) << asDateTime.hour() << std::setfill('0') << std::setw(2) << asDateTime.minute();
726 setValue(Id3v2FrameIds::lTime, TagValue(time.str()));
727 if (asDateTime.second() || asDateTime.millisecond()) {
728 diag.emplace_back(DiagLevel::Warning,
729 "The recording time field (TRDA) has been truncated to full minutes when converting to corresponding fields for older ID3v2 "
730 "versions.",
731 diagContext);
732 }
733 } catch (const ConversionException &e) {
734 try {
735 diag.emplace_back(DiagLevel::Critical,
736 argsToString("Unable to convert recording time field (TRDA) with the value \"", recordingTime.toString(),
737 "\" to corresponding fields for older ID3v2 versions: ", e.what()),
738 diagContext);
739 } catch (const ConversionException &) {
740 diag.emplace_back(DiagLevel::Critical,
741 argsToString("Unable to convert recording time field (TRDA) to corresponding fields for older ID3v2 versions: ", e.what()),
742 diagContext);
743 }
744 }
745 // -> get rid of lRecordingTime
747}
748
753Id3v2TagMaker::Id3v2TagMaker(Id3v2Tag &tag, Diagnostics &diag)
754 : m_tag(tag)
755 , m_framesSize(0)
756{
757 static const string context("making ID3v2 tag");
758
759 // check if version is supported
760 // (the version could have been changed using setVersion())
761 if (!tag.isVersionSupported()) {
762 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag version isn't supported.", context);
763 throw VersionNotSupportedException();
764 }
765
766 if (m_tag.m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
767 tag.prepareRecordDataForMaking(context, diag);
768 }
769
770 // prepare frames
771 m_maker.reserve(tag.fields().size());
772 for (auto &pair : tag.fields()) {
773 try {
774 m_maker.emplace_back(pair.second.prepareMaking(tag.majorVersion(), diag));
775 m_framesSize += m_maker.back().requiredSize();
776 } catch (const Failure &) {
777 }
778 }
779
780 // calculate required size
781 // -> header + size of frames
782 m_requiredSize = 10 + m_framesSize;
783}
784
792void Id3v2TagMaker::make(std::ostream &stream, std::uint32_t padding, Diagnostics &diag)
793{
794 CPP_UTILITIES_UNUSED(diag)
795
796 BinaryWriter writer(&stream);
797
798 // write header
799 // -> signature
800 writer.writeUInt24BE(0x494433u);
801 // -> version
802 writer.writeByte(m_tag.majorVersion());
803 writer.writeByte(m_tag.revisionVersion());
804 // -> flags, but without extended header or compression bit set
805 writer.writeByte(m_tag.flags() & 0xBF);
806 // -> size (excluding header)
807 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
808
809 // write frames
810 for (auto &maker : m_maker) {
811 maker.make(writer);
812 }
813
814 // write padding
815 for (; padding; --padding) {
816 stream.put(0);
817 }
818}
819
820} // 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:792
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:590
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:599
KnownField internallyGetKnownField(const IdentifierType &id) const
Definition: id3v2tag.cpp:251
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:351
Id3v2TagMaker prepareMaking(Diagnostics &diag)
Prepares making.
Definition: id3v2tag.cpp:578
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:458
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:213
std::uint64_t m_size
Definition: tag.h:214
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:90
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:618