Tag Parser 12.2.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
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 converts 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;
170 return lUnsynchronizedLyrics;
172 return lSynchronizedLyrics;
174 return lContentGroupDescription;
176 return lRecordLabel;
178 return lComposer;
180 return lPlayCounter;
182 return lRating;
184 return lAlbumArtist;
186 return lRemixedBy;
188 return lCopyright;
190 return lTaggingTime;
192 return lEncodingTime;
194 return lOriginalReleaseTime;
195 case KnownField::Mood:
196 return lMood;
197 default:;
198 }
199 } else {
200 switch (field) {
202 return sAlbum;
204 return sArtist;
206 return sComment;
208 return lRecordingTime; // (de)serializer converts to/from sYear/sRecordingDates/sDate/sTime
210 return sTitle;
212 return sGenre;
214 return sTrackPosition;
216 return sDiskPosition;
218 return sEncoder;
219 case KnownField::Bpm:
220 return sBpm;
222 return sCover;
224 return sWriter;
226 return sLength;
228 return sLanguage;
230 return sEncoderSettings;
232 return sUnsynchronizedLyrics;
234 return sSynchronizedLyrics;
236 return sContentGroupDescription;
238 return sRecordLabel;
240 return sComposer;
242 return sPlayCounter;
244 return sRating;
246 return sAlbumArtist;
248 return sRemixedBy;
250 return sCopyright;
251 default:;
252 }
253 }
254 return 0;
255}
256
257KnownField Id3v2Tag::internallyGetKnownField(const IdentifierType &id) const
258{
259 using namespace Id3v2FrameIds;
260 switch (id) {
261 case lAlbum:
262 return KnownField::Album;
263 case lArtist:
264 return KnownField::Artist;
265 case lComment:
266 return KnownField::Comment;
267 case lRecordingTime:
268 case lYear:
270 case lTitle:
271 return KnownField::Title;
272 case lGenre:
273 return KnownField::Genre;
274 case lTrackPosition:
276 case lDiskPosition:
278 case lEncoder:
279 return KnownField::Encoder;
280 case lBpm:
281 return KnownField::Bpm;
282 case lCover:
283 return KnownField::Cover;
284 case lWriter:
286 case lLanguage:
288 case lLength:
289 return KnownField::Length;
290 case lEncoderSettings:
292 case lUnsynchronizedLyrics:
293 return KnownField::Lyrics;
294 case lSynchronizedLyrics:
296 case lAlbumArtist:
298 case lRemixedBy:
300 case lCopyright:
302 case lContentGroupDescription:
304 case lRecordLabel:
306 case lTaggingTime:
308 case lEncodingTime:
310 case lOriginalReleaseTime:
312 case lMood:
313 return KnownField::Mood;
314 case lPlayCounter:
316 case lRating:
317 return KnownField::Rating;
318 case lISRC:
319 return KnownField::ISRC;
320 case sAlbum:
321 return KnownField::Album;
322 case sArtist:
323 return KnownField::Artist;
324 case sComment:
325 return KnownField::Comment;
326 case sYear:
328 case sTitle:
329 return KnownField::Title;
330 case sGenre:
331 return KnownField::Genre;
332 case sTrackPosition:
334 case sEncoder:
335 return KnownField::Encoder;
336 case sBpm:
337 return KnownField::Bpm;
338 case sCover:
339 return KnownField::Cover;
340 case sWriter:
342 case sLanguage:
344 case sLength:
345 return KnownField::Length;
346 case sEncoderSettings:
348 case sUnsynchronizedLyrics:
349 return KnownField::Lyrics;
350 case sSynchronizedLyrics:
352 case sAlbumArtist:
354 case sRecordLabel:
356 case sRemixedBy:
358 case sCopyright:
360 case sPlayCounter:
362 case sRating:
363 return KnownField::Rating;
364 case sISRC:
365 return KnownField::ISRC;
366 default:
367 return KnownField::Invalid;
368 }
369}
370
372{
373 using namespace Id3v2FrameIds;
374 switch (id) {
375 case lLength:
376 case sLength:
378 case lBpm:
379 case sBpm:
380 case lYear:
381 case sYear:
382 case lPlayCounter:
383 case sPlayCounter:
385 case lTrackPosition:
386 case sTrackPosition:
387 case lDiskPosition:
389 case lCover:
390 case sCover:
392 case lRating:
393 case sRating:
395 default:
397 return TagDataType::Text;
398 } else {
400 }
401 }
402}
403
410void Id3v2Tag::convertOldRecordDateFields(const std::string &diagContext, Diagnostics &diag)
411{
412 // skip if it is a v2.4.0 tag and lRecordingTime is present
413 if (majorVersion() >= 4 && fields().find(Id3v2FrameIds::lRecordingTime) != fields().cend()) {
414 return;
415 }
416
417 // parse values of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime fields
418 auto expr = DateTimeExpression();
419 auto year = 1, month = 1, day = 1, hour = 0, minute = 0;
420 if (const auto &v = value(Id3v2FrameIds::lYear)) {
421 expr.parts |= DateTimeParts::Year;
422 try {
423 year = v.toInteger();
424 } catch (const ConversionException &e) {
425 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse year from \"TYER\" frame: ", e.what()), diagContext);
426 }
427 }
428 if (const auto &v = value(Id3v2FrameIds::lDate)) {
429 expr.parts |= DateTimeParts::Day | DateTimeParts::Month;
430 try {
431 auto str = v.toString();
432 if (str.size() != 4) {
433 throw ConversionException("format is not DDMM");
434 }
435 day = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
436 month = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
437 } catch (const ConversionException &e) {
438 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse month and day from \"TDAT\" frame: ", e.what()), diagContext);
439 }
440 }
441 if (const auto &v = value(Id3v2FrameIds::lTime)) {
442 expr.parts |= DateTimeParts::Hour | DateTimeParts::Minute;
443 try {
444 auto str = v.toString();
445 if (str.size() != 4) {
446 throw ConversionException("format is not HHMM");
447 }
448 hour = stringToNumber<unsigned short>(std::string_view(str.data() + 0, 2));
449 minute = stringToNumber<unsigned short>(std::string_view(str.data() + 2, 2));
450 } catch (const ConversionException &e) {
451 diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse hour and minute from \"TIME\" frame: ", +e.what()), diagContext);
452 }
453 }
454
455 // set the field values as DateTime
456 if (expr.parts == DateTimeParts::None) {
457 return;
458 }
459 try {
460 expr.value = DateTime::fromDateAndTime(year, month, day, hour, minute);
462 } catch (const ConversionException &e) {
463 try {
464 // try to set at least the year
465 expr.parts = DateTimeParts::Year;
466 expr.value = DateTime::fromDate(year);
468 diag.emplace_back(DiagLevel::Critical,
469 argsToString(
470 "Unable to parse the full date of the recording. Only the 'Year' frame could be parsed; related frames failed: ", e.what()),
471 diagContext);
472 } catch (const ConversionException &) {
473 }
474 diag.emplace_back(
475 DiagLevel::Critical, argsToString("Unable to parse a valid date from the 'Year' frame and related frames: ", e.what()), diagContext);
476 }
477}
478
486void Id3v2Tag::parse(istream &stream, const std::uint64_t maximalSize, Diagnostics &diag)
487{
488 // prepare parsing
489 static const string context("parsing ID3v2 tag");
490 BinaryReader reader(&stream);
491 const auto startOffset = static_cast<std::uint64_t>(stream.tellg());
492
493 // check whether the header is truncated
494 if (maximalSize && maximalSize < 10) {
495 diag.emplace_back(DiagLevel::Critical, "ID3v2 header is truncated (at least 10 bytes expected).", context);
497 }
498
499 // read signature: ID3
500 if (reader.readUInt24BE() != 0x494433u) {
501 diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
502 throw InvalidDataException();
503 }
504 // read header data
505 const std::uint8_t majorVersion = reader.readByte();
506 const std::uint8_t revisionVersion = reader.readByte();
508 m_flags = reader.readByte();
509 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
510 m_size = 10 + m_sizeExcludingHeader;
511 if (m_sizeExcludingHeader == 0) {
512 diag.emplace_back(DiagLevel::Warning, "ID3v2 tag seems to be empty.", context);
513 return;
514 }
515
516 // check if the version
517 if (!isVersionSupported()) {
518 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
520 }
521
522 // read extended header (if present)
523 if (hasExtendedHeader()) {
524 if (maximalSize && maximalSize < 14) {
525 diag.emplace_back(DiagLevel::Critical, "Extended header denoted but not present.", context);
527 }
528 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
529 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
530 diag.emplace_back(DiagLevel::Critical, "Extended header is invalid/truncated.", context);
532 }
533 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
534 }
535
536 // how many bytes remain for frames and padding?
537 std::uint32_t bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
538 if (maximalSize && bytesRemaining > maximalSize) {
539 bytesRemaining = static_cast<std::uint32_t>(maximalSize);
540 diag.emplace_back(DiagLevel::Critical, "Frames are truncated.", context);
541 }
542
543 // read frames
544 auto pos = static_cast<std::uint64_t>(stream.tellg());
545 while (bytesRemaining) {
546 // seek to next frame
547 stream.seekg(static_cast<streamoff>(pos));
548 // parse frame
549 Id3v2Frame frame;
550 try {
551 frame.parse(reader, majorVersion, bytesRemaining, diag);
552 if (Id3v2FrameIds::isTextFrame(frame.id()) && fields().count(frame.id()) == 1) {
553 diag.emplace_back(DiagLevel::Warning, "The text frame " % frame.idToString() + " exists more than once.", context);
554 }
555 fields().emplace(frame.id(), std::move(frame));
556 } catch (const NoDataFoundException &) {
557 if (frame.hasPaddingReached()) {
558 m_paddingSize = startOffset + m_size - pos;
559 break;
560 }
561 } catch (const Failure &) {
562 }
563
564 // calculate next frame offset
565 if (frame.totalSize() <= bytesRemaining) {
566 pos += frame.totalSize();
567 bytesRemaining -= frame.totalSize();
568 } else {
569 pos += bytesRemaining;
570 bytesRemaining = 0;
571 }
572 }
573
574 if (m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
575 convertOldRecordDateFields(context, diag);
576 }
577
578 // check for extended header
579 if (!hasFooter()) {
580 return;
581 }
582 if (maximalSize && m_size + 10 < maximalSize) {
583 // the footer does not provide additional information, just check the signature
584 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
585 if (reader.readUInt24LE() != 0x494433u) {
586 diag.emplace_back(DiagLevel::Critical, "Footer signature is invalid.", context);
587 }
588 // skip remaining footer
589 stream.seekg(7, ios_base::cur);
590 } else {
591 diag.emplace_back(DiagLevel::Critical, "Footer denoted but not present.", context);
593 }
594}
595
607{
608 return Id3v2TagMaker(*this, diag);
609}
610
618void Id3v2Tag::make(ostream &stream, std::uint32_t padding, Diagnostics &diag)
619{
620 prepareMaking(diag).make(stream, padding, diag);
621}
622
627void Id3v2Tag::setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
628{
629 m_majorVersion = majorVersion;
630 m_revisionVersion = revisionVersion;
631 m_version = argsToString('2', '.', majorVersion, '.', revisionVersion);
632}
633
646bool FrameComparer::operator()(std::uint32_t lhs, std::uint32_t rhs) const
647{
648 if (lhs == rhs) {
649 return false;
650 }
651
652 const bool lhsLong = Id3v2FrameIds::isLongId(lhs);
653 const bool rhsLong = Id3v2FrameIds::isLongId(rhs);
654 if (lhsLong != rhsLong) {
655 if (!lhsLong) {
657 if (!lhs) {
658 return true;
659 }
660 } else if (!rhsLong) {
662 if (!rhs) {
663 return true;
664 }
665 }
666 }
667
669 return true;
670 }
672 return false;
673 }
674 if (lhs == Id3v2FrameIds::lTitle || lhs == Id3v2FrameIds::sTitle) {
675 return true;
676 }
677 if (rhs == Id3v2FrameIds::lTitle || rhs == Id3v2FrameIds::sTitle) {
678 return false;
679 }
680
681 const bool lhstextfield = Id3v2FrameIds::isTextFrame(lhs);
682 const bool rhstextfield = Id3v2FrameIds::isTextFrame(rhs);
683 if (lhstextfield && !rhstextfield) {
684 return true;
685 }
686 if (!lhstextfield && rhstextfield) {
687 return false;
688 }
689
690 if (lhs == Id3v2FrameIds::lCover || lhs == Id3v2FrameIds::sCover) {
691 return false;
692 }
693 if (rhs == Id3v2FrameIds::lCover || rhs == Id3v2FrameIds::sCover) {
694 return true;
695 }
696 return lhs < rhs;
697}
698
709void Id3v2Tag::removeOldRecordDateRelatedFields()
710{
712 fields().erase(field);
713 }
714}
715
719void Id3v2Tag::prepareRecordDataForMaking(const std::string &diagContext, Diagnostics &diag)
720{
721 // get rid of lYear/lRecordingDates/lDate/lTime/sYear/sRecordingDates/sDate/sTime if writing v2.4.0 or newer
722 // note: If the tag was initially v2.3.0 or older the "old" fields have already been converted to lRecordingTime when
723 // parsing and the generic accessors propose using lRecordingTime in any case.
724 if (majorVersion() >= 4) {
725 removeOldRecordDateRelatedFields();
726 return;
727 }
728
729 // convert lRecordingTime to old fields for v2.3.0 and older
730 const auto recordingTimeFieldIterator = fields().find(Id3v2FrameIds::lRecordingTime);
731 // -> If the auto-created lRecordingTime field (see note above) has been completely removed write the old fields as-is.
732 // This allows one to bypass this handling and set the old fields explicitly.
733 if (recordingTimeFieldIterator == fields().cend()) {
734 return;
735 }
736 // -> simply remove all old fields if lRecordingTime is set to an empty value
737 const auto &recordingTime = recordingTimeFieldIterator->second.value();
738 if (recordingTime.isEmpty()) {
739 removeOldRecordDateRelatedFields();
740 return;
741 }
742 // -> convert lRecordingTime (which is supposed to be an ISO string) to a DateTime
743 try {
744 const auto dateTimeExpr = recordingTime.toDateTimeExpression();
745 const auto &asDateTime = dateTimeExpr.value;
746 // -> remove any existing old fields to avoid any leftovers
747 removeOldRecordDateRelatedFields();
748 // -> assign old fields from parsed DateTime
749 std::stringstream year, date, time;
750 if (dateTimeExpr.parts & DateTimeParts::Year) {
751 year << std::setfill('0') << std::setw(4) << asDateTime.year();
752 setValue(Id3v2FrameIds::lYear, TagValue(year.str()));
753 }
754 if (dateTimeExpr.parts & (DateTimeParts::Day | DateTimeParts::Month)) {
755 date << std::setfill('0') << std::setw(2) << asDateTime.day() << std::setfill('0') << std::setw(2) << asDateTime.month();
756 setValue(Id3v2FrameIds::lDate, TagValue(date.str()));
757 }
758 if (dateTimeExpr.parts & DateTimeParts::Time) {
759 time << std::setfill('0') << std::setw(2) << asDateTime.hour() << std::setfill('0') << std::setw(2) << asDateTime.minute();
760 setValue(Id3v2FrameIds::lTime, TagValue(time.str()));
761 }
762 if (dateTimeExpr.parts & (DateTimeParts::Second | DateTimeParts::SubSecond)) {
763 diag.emplace_back(DiagLevel::Warning,
764 "The recording time field (TDRC) has been truncated to full minutes when converting to corresponding fields for older ID3v2 "
765 "versions.",
766 diagContext);
767 }
768 } catch (const ConversionException &e) {
769 try {
770 diag.emplace_back(DiagLevel::Critical,
771 argsToString("Unable to convert recording time field (TDRC) with the value \"", recordingTime.toString(),
772 "\" to corresponding fields for older ID3v2 versions: ", e.what()),
773 diagContext);
774 } catch (const ConversionException &) {
775 diag.emplace_back(DiagLevel::Critical,
776 argsToString("Unable to convert recording time field (TRDA) to corresponding fields for older ID3v2 versions: ", e.what()),
777 diagContext);
778 }
779 }
780 // -> get rid of lRecordingTime
782}
783
788Id3v2TagMaker::Id3v2TagMaker(Id3v2Tag &tag, Diagnostics &diag)
789 : m_tag(tag)
790 , m_framesSize(0)
791{
792 static const string context("making ID3v2 tag");
793
794 // check if version is supported
795 // (the version could have been changed using setVersion())
796 if (!tag.isVersionSupported()) {
797 diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag version isn't supported.", context);
798 throw VersionNotSupportedException();
799 }
800
801 if (m_tag.m_handlingFlags & Id3v2HandlingFlags::ConvertRecordDateFields) {
802 tag.prepareRecordDataForMaking(context, diag);
803 }
804
805 // prepare frames
806 m_maker.reserve(tag.fields().size());
807 for (auto &pair : tag.fields()) {
808 try {
809 m_maker.emplace_back(pair.second.prepareMaking(tag.majorVersion(), diag));
810 m_framesSize += m_maker.back().requiredSize();
811 } catch (const Failure &) {
812 }
813 }
814
815 // calculate required size
816 // -> header + size of frames
817 m_requiredSize = 10 + m_framesSize;
818}
819
827void Id3v2TagMaker::make(std::ostream &stream, std::uint32_t padding, Diagnostics &diag)
828{
829 CPP_UTILITIES_UNUSED(diag)
830
831 BinaryWriter writer(&stream);
832
833 // write header
834 // -> signature
835 writer.writeUInt24BE(0x494433u);
836 // -> version
837 writer.writeByte(m_tag.majorVersion());
838 writer.writeByte(m_tag.revisionVersion());
839 // -> flags, but without extended header or compression bit set
840 writer.writeByte(m_tag.flags() & 0xBF);
841 // -> size (excluding header)
842 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
843
844 // write frames
845 for (auto &maker : m_maker) {
846 maker.make(writer);
847 }
848
849 // write padding
850 for (; padding; --padding) {
851 stream.put(0);
852 }
853}
854
855} // 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:827
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:618
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:627
KnownField internallyGetKnownField(const IdentifierType &id) const
Definition id3v2tag.cpp:257
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:371
Id3v2TagMaker prepareMaking(Diagnostics &diag)
Prepares making.
Definition id3v2tag.cpp:606
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:486
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
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:204
std::uint64_t m_size
Definition tag.h:205
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:646