38#include <c++utilities/chrono/timespan.h>
39#include <c++utilities/conversion/stringconversion.h>
40#include <c++utilities/io/path.h>
50#include <system_error>
53using namespace std::placeholders;
85 , m_containerOffset(0)
95 , m_preferredPadding(0)
100 , m_maxFullParseSize(0x3200000)
142 CPP_UTILITIES_UNUSED(progress)
149 static const string context(
"parsing file header");
155 m_containerOffset = 0;
156 std::size_t bytesSkippedBeforeContainer = 0;
157 std::streamoff id3v2Size = 0;
161 const char *
const buffEnd = buff +
sizeof(buff), *buffOffset;
162startParsingSignature:
168 stream().seekg(m_containerOffset, ios_base::beg);
169 stream().read(buff,
sizeof(buff));
176 std::size_t bytesSkipped = 0;
177 for (buffOffset = buff; buffOffset != buffEnd && !(*buffOffset); ++buffOffset, ++bytesSkipped)
179 if (bytesSkipped >= 4) {
181 m_containerOffset +=
static_cast<std::streamoff
>(bytesSkipped);
182 m_paddingSize += bytesSkipped;
185 if ((bytesSkippedBeforeContainer += bytesSkipped) >= 0x800u) {
188 m_containerOffset = id3v2Size;
193 goto startParsingSignature;
197 switch ((m_containerFormat =
parseSignature(buff,
sizeof(buff)))) {
200 m_actualId3v2TagOffsets.push_back(m_containerOffset);
201 if (m_actualId3v2TagOffsets.size() == 2) {
202 diag.emplace_back(
DiagLevel::Warning,
"There is more than just one ID3v2 header at the beginning of the file.", context);
206 stream().seekg(m_containerOffset + 5, ios_base::beg);
210 m_containerOffset += toNormalInt(BE::toInt<std::uint32_t>(buff + 1)) + 10;
211 if ((*buff) & 0x10) {
213 m_containerOffset += 10;
215 id3v2Size = m_containerOffset;
218 goto startParsingSignature;
223 m_container = make_unique<Mp4Container>(*
this, m_containerOffset);
227 diag.emplace_back(
DiagLevel::Information,
"Validating the MP4 element structure has been aborted.", context);
235 auto container = make_unique<MatroskaContainer>(*
this, m_containerOffset);
246 container->validateElementStructure(diag, progress, &m_paddingSize);
247 container->validateIndex(diag, progress);
250 diag.emplace_back(
DiagLevel::Information,
"Validating the Matroska element structure has been aborted.", context);
259 m_container = make_unique<OggContainer>(*
this, m_containerOffset);
266 if (
const auto apeEnd = m_containerOffset + 32 + LE::toUInt32(buff + 12); apeEnd <= static_cast<std::streamoff>(
size())) {
269 argsToString(
"Found an APE tag at the beginning of the file at offset ", m_containerOffset,
270 ". This tag format is not supported and the tag will therefore be ignored. It will NOT be preserved when saving as "
271 "placing an APE tag at the beginning of a file is strongly unrecommended."),
274 m_containerOffset = apeEnd;
275 goto startParsingSignature;
282 if (
size() > 0x107) {
285 if (buff[0] == 0x75 && buff[1] == 0x73 && buff[2] == 0x74 && buff[3] == 0x61 && buff[4] == 0x72 && buff[5] == 0x00) {
299 if (bytesSkippedBeforeContainer) {
300 diag.emplace_back(
DiagLevel::Warning, argsToString(bytesSkippedBeforeContainer,
" bytes of junk skipped"), context);
330 static const string context(
"parsing tracks");
335 m_container->parseTracks(diag, progress);
341 switch (m_containerFormat) {
343 m_singleTrack = make_unique<AdtsStream>(
stream(), m_containerOffset);
346 m_singleTrack = make_unique<FlacStream>(*
this, m_containerOffset);
349 m_singleTrack = make_unique<IvfStream>(
stream(), m_containerOffset);
352 m_singleTrack = make_unique<MpegAudioFrameStream>(
stream(), m_containerOffset);
355 m_singleTrack = make_unique<WaveAudioStream>(
stream(), m_containerOffset);
365 m_singleTrack->setSize(m_effectiveSize);
367 m_singleTrack->parseHeader(diag, progress);
370 switch (m_containerFormat) {
380 diag.emplace_back(
DiagLevel::Information,
"Parsing tracks is not implemented for the container format of the file.", context);
408 static const string context(
"parsing tag");
413 m_id3v1Tag = make_unique<Id3v1Tag>();
416 m_id3v1Tag->parse(
stream(), diag);
431 if (
constexpr auto apeHeaderSize = 32;
effectiveSize >= apeHeaderSize) {
433 char buffer[apeHeaderSize];
434 stream().seekg(footerOffset, std::ios_base::beg);
435 stream().read(buffer,
sizeof(buffer));
436 if (BE::toInt<std::uint64_t>(buffer) == 0x4150455441474558ul ) {
438 const auto tagSize =
static_cast<std::streamoff
>(LE::toInt<std::uint32_t>(buffer + 12));
439 const auto flags = LE::toInt<std::uint32_t>(buffer + 20);
445 if ((flags & 0x80000000u) && (apeHeaderSize <=
effectiveSize)) {
449 argsToString(
"Found an APE tag at the end of the file at offset ", (footerOffset - tagSize),
450 ". This tag format is not supported and the tag will therefore be ignored. It will be preserved when saving as-is."),
457 for (
const auto offset : m_actualId3v2TagOffsets) {
458 auto id3v2Tag = make_unique<Id3v2Tag>();
459 stream().seekg(offset, ios_base::beg);
461 id3v2Tag->parse(
stream(),
size() -
static_cast<std::uint64_t
>(offset), diag);
462 m_paddingSize += id3v2Tag->paddingSize();
472 m_id3v2Tags.emplace_back(id3v2Tag.release());
476 m_effectiveSize =
static_cast<std::uint64_t
>(
effectiveSize - m_containerOffset);
483 m_tagsParsingStatus = m_tracksParsingStatus;
486 }
else if (m_container) {
487 m_container->parseTags(diag, progress);
502 diag.emplace_back(
DiagLevel::Information,
"Parsing tags is not implemented for the container format of the file.", context);
504 diag.emplace_back(
DiagLevel::Information,
"Parsing tags from container/streams has been aborted.", context);
527 static const string context(
"parsing chapters");
534 m_container->parseChapters(diag, progress);
538 diag.emplace_back(
DiagLevel::Information,
"Parsing chapters is not implemented for the container format of the file.", context);
560 static const string context(
"parsing attachments");
567 m_container->parseAttachments(diag, progress);
571 diag.emplace_back(
DiagLevel::Information,
"Parsing attachments is not implemented for the container format of the file.", context);
625 const auto flags(settings.
flags);
626 const auto targetsRequired = !requiredTargets.empty() && (requiredTargets.size() != 1 || !requiredTargets.front().isEmpty());
627 auto targetsSupported =
false;
630 if (targetsRequired) {
632 if (m_container->tagCount()) {
634 targetsSupported = m_container->tag(0)->supportsTarget();
637 auto *
const tag = m_container->createTag();
638 if (tag && (targetsSupported = tag->supportsTarget())) {
639 tag->setTarget(requiredTargets.front());
642 if (targetsSupported) {
643 for (
const auto &target : requiredTargets) {
644 m_container->createTag(target);
649 m_container->createTag();
655 switch (m_containerFormat) {
677 for (
const auto &id3v2Tag :
id3v2Tags()) {
690 id3v2Tag->insertValues(*
id3v1Tag(),
true);
745 static const string context(
"making file");
747 bool previousParsingSuccessful =
true;
753 previousParsingSuccessful =
false;
754 diag.emplace_back(
DiagLevel::Critical,
"Tags have to be parsed without critical errors before changes can be applied.", context);
761 previousParsingSuccessful =
false;
762 diag.emplace_back(
DiagLevel::Critical,
"Tracks have to be parsed without critical errors before changes can be applied.", context);
764 if (!previousParsingSuccessful) {
770 diag.emplace_back(
DiagLevel::Warning,
"Assigned ID3v1 tag can't be attached and will be ignored.", context);
773 diag.emplace_back(
DiagLevel::Warning,
"Assigned ID3v2 tag can't be attached and will be ignored.", context);
778 m_container->makeFile(diag, progress);
787 makeMp3File(diag, progress);
812 unsigned int version = 0;
813 switch (m_containerFormat) {
820 bool onlyOpus =
true, onlySpeex =
true;
834 }
else if (onlySpeex) {
845 version = m_singleTrack->format().sub;
866 switch (m_containerFormat) {
892 vector<AbstractTrack *> res;
894 size_t containerTrackCount = 0;
899 trackCount += (containerTrackCount = m_container->trackCount());
904 res.push_back(m_singleTrack.get());
906 for (
size_t i = 0; i != containerTrackCount; ++i) {
907 res.push_back(m_container->track(i));
925 if (m_singleTrack && m_singleTrack->mediaType() == type) {
927 }
else if (m_container) {
928 for (
size_t i = 0, count = m_container->trackCount(); i != count; ++i) {
929 if (m_container->track(i)->mediaType() == type) {
949 return m_container->duration();
950 }
else if (m_singleTrack) {
951 return m_singleTrack->duration();
971 return 0.0078125 *
static_cast<double>(
size()) /
duration.totalSeconds();
986 unordered_set<string> res;
988 for (
size_t i = 0, count = m_container->trackCount(); i != count; ++i) {
994 res.emplace(language);
997 }
else if (m_singleTrack && (type ==
MediaType::Unknown || m_singleTrack->mediaType() == type)) {
998 if (
const auto &language = m_singleTrack->locale().someAbbreviatedName(); !language.empty()) {
999 res.emplace(language);
1019 const size_t trackCount = m_container->trackCount();
1020 vector<string> parts;
1023 const string description(m_container->track(i)->description());
1024 if (!description.empty()) {
1025 parts.emplace_back(std::move(description));
1028 return joinStrings(parts,
" / ");
1029 }
else if (m_singleTrack) {
1030 return m_singleTrack->description();
1077 m_id3v1Tag = make_unique<Id3v1Tag>();
1079 return m_id3v1Tag.get();
1097 for (
auto i = m_id3v2Tags.begin(), end = m_id3v2Tags.end(); i != end; ++i) {
1098 if (i->get() == tag) {
1099 m_id3v2Tags.erase(i);
1119 m_id3v2Tags.clear();
1140 if (m_id3v2Tags.empty()) {
1141 m_id3v2Tags.emplace_back(make_unique<Id3v2Tag>());
1143 return m_id3v2Tags.front().get();
1167 return m_container->removeTag(tag);
1172 auto *
const flacStream(
static_cast<FlacStream *
>(m_singleTrack.get()));
1173 if (flacStream->vorbisComment() == tag) {
1174 return flacStream->removeVorbisComment();
1179 if (m_id3v1Tag.get() == tag) {
1183 for (
auto i = m_id3v2Tags.begin(), end = m_id3v2Tags.end(); i != end; ++i) {
1184 if (i->get() == tag) {
1185 m_id3v2Tags.erase(i);
1201 m_container->removeAllTags();
1207 m_id3v2Tags.clear();
1215 if (m_container && m_container->chapterCount()) {
1218 switch (m_containerFormat) {
1232 if (m_container && m_container->attachmentCount()) {
1235 switch (m_containerFormat) {
1252 switch (m_containerFormat) {
1270 switch (m_containerFormat) {
1297 && m_container->tagCount() > 0
1314 static const std::vector<std::unique_ptr<MatroskaTag>> empty;
1338 vector<AbstractChapter *> res;
1340 const size_t count = m_container->chapterCount();
1342 for (
size_t i = 0; i != count; ++i) {
1343 res.push_back(m_container->chapter(i));
1356 vector<AbstractAttachment *> res;
1358 const size_t count = m_container->attachmentCount();
1360 for (
size_t i = 0; i != count; ++i) {
1361 res.push_back(m_container->attachment(i));
1382 m_containerOffset = 0;
1389 m_id3v2Tags.clear();
1390 m_actualId3v2TagOffsets.clear();
1392 m_container.reset();
1393 m_singleTrack.reset();
1401 static constexpr auto bufferSize =
static_cast<std::uint64_t
>(1024 * 8);
1402 for (
constexpr char buffer[bufferSize] = {};
size;) {
1403 const auto count = std::min(bufferSize,
size);
1404 outputStream.write(buffer,
static_cast<std::streamsize
>(count));
1427 auto begin = m_id3v2Tags.begin(), end = m_id3v2Tags.end();
1432 auto isecond = begin + 1;
1433 if (isecond == end) {
1436 for (
auto i = isecond; i != end; ++i) {
1439 m_id3v2Tags.erase(isecond, end);
1496 switch (m_containerFormat) {
1503 if (m_singleTrack) {
1523 switch (m_containerFormat) {
1532 if (m_singleTrack) {
1553 tags.push_back(m_id3v1Tag.get());
1555 for (
const unique_ptr<Id3v2Tag> &tag : m_id3v2Tags) {
1556 tags.push_back(tag.get());
1564 for (
size_t i = 0, count = m_container->tagCount(); i < count; ++i) {
1565 tags.push_back(m_container->tag(i));
1580 auto res = vector<Tag *>();
1609 tags.push_back(m_id3v1Tag.get());
1611 for (
const unique_ptr<Id3v2Tag> &tag : m_id3v2Tags) {
1613 tags.push_back(tag.get());
1624 for (
size_t i = 0, count = m_container->tagCount(); i < count; ++i) {
1625 if (
auto *
const tag = m_container->tag(i); tag->size()) {
1626 tags.push_back(tag);
1642 auto res = vector<Tag *>();
1661 static const string context(
"making MP3/FLAC file");
1664 if (!
isForcingRewrite() && m_id3v2Tags.empty() && m_actualId3v2TagOffsets.empty() && m_saveFilePath.empty()
1673 progress.
updateStep(
"Removing ID3v1 tag ...");
1675 auto ec = std::error_code();
1680 diag.emplace_back(
DiagLevel::Critical,
"Unable to truncate file to remove ID3v1 tag: " + ec.message(), context);
1681 throw std::ios_base::failure(
"Unable to truncate file to remove ID3v1 tag.");
1687 progress.
updateStep(
"Updating existing ID3v1 tag ...");
1690 stream().seekp(-128, ios_base::end);
1692 m_id3v1Tag->make(
stream(), diag);
1693 }
catch (
const Failure &) {
1697 progress.
updateStep(
"Adding new ID3v1 tag ...");
1700 stream().seekp(0, ios_base::end);
1702 m_id3v1Tag->make(
stream(), diag);
1703 }
catch (
const Failure &) {
1715 progress.
updateStep(flacStream ?
"Updating FLAC tags ..." :
"Updating ID3v2 tags ...");
1718 auto makers = std::vector<Id3v2TagMaker>();
1719 makers.reserve(m_id3v2Tags.size());
1720 auto tagsSize = std::uint64_t();
1721 for (
auto &tag : m_id3v2Tags) {
1723 makers.emplace_back(tag->prepareMaking(diag));
1724 tagsSize += makers.back().requiredSize();
1725 }
catch (
const Failure &) {
1730 auto streamOffset = std::uint32_t();
1731 auto flacMetaData = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
1732 flacMetaData.exceptions(std::ios_base::badbit | std::ios_base::failbit);
1733 auto startOfLastMetaDataBlock = std::streamoff();
1736 startOfLastMetaDataBlock = flacStream->makeHeader(flacMetaData, diag);
1737 tagsSize +=
static_cast<std::uint64_t
>(flacMetaData.tellp());
1738 streamOffset = flacStream->streamOffset();
1741 streamOffset =
static_cast<std::uint32_t
>(m_containerOffset);
1745 auto rewriteRequired =
isForcingRewrite() || !m_saveFilePath.empty() || (tagsSize > streamOffset);
1746 auto padding = std::size_t();
1747 if (!rewriteRequired) {
1750 padding = streamOffset - tagsSize;
1753 rewriteRequired =
true;
1756 if (makers.empty() && !flacStream) {
1762 rewriteRequired =
true;
1764 }
else if (rewriteRequired) {
1768 }
else if (makers.empty() && flacStream && padding && padding < 4) {
1772 rewriteRequired =
true;
1774 if (rewriteRequired && flacStream && makers.empty() && padding) {
1779 progress.
updateStep(rewriteRequired ?
"Preparing streams for rewriting ..." :
"Preparing streams for updating ...");
1783 string originalPath =
path(), backupPath;
1784 NativeFileStream &outputStream =
stream();
1785 NativeFileStream backupStream;
1787 if (rewriteRequired) {
1788 if (m_saveFilePath.empty()) {
1793 outputStream.open(originalPath, ios_base::out | ios_base::binary | ios_base::trunc);
1794 }
catch (
const std::ios_base::failure &failure) {
1796 DiagLevel::Critical, argsToString(
"Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context);
1803 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
1806 }
catch (
const std::ios_base::failure &failure) {
1807 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Opening streams to write output file failed: ", failure.what()), context);
1817 }
catch (
const std::ios_base::failure &failure) {
1818 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Opening the file with write permissions failed: ", failure.what()), context);
1827 if (padding > numeric_limits<std::uint32_t>::max()) {
1828 padding = numeric_limits<std::uint32_t>::max();
1830 DiagLevel::Critical, argsToString(
"Preferred padding is not supported. Setting preferred padding to ", padding,
'.'), context);
1833 if (!makers.empty()) {
1835 progress.
updateStep(
"Writing ID3v2 tag ...");
1836 for (
auto i = makers.begin(), end = makers.end() - 1; i != end; ++i) {
1837 i->make(outputStream, 0, diag);
1840 makers.back().make(outputStream, (flacStream && padding && padding < 4) ? 0 :
static_cast<std::uint32_t
>(padding), diag);
1844 if (padding && startOfLastMetaDataBlock) {
1846 flacMetaData.seekg(startOfLastMetaDataBlock);
1847 flacMetaData.seekp(startOfLastMetaDataBlock);
1848 flacMetaData.put(
static_cast<std::uint8_t
>(flacMetaData.peek()) & (0x80u - 1));
1849 flacMetaData.seekg(0);
1853 outputStream << flacMetaData.rdbuf();
1857 flacStream->makePadding(outputStream,
static_cast<std::uint32_t
>(padding),
true, diag);
1861 if (makers.empty() && !flacStream) {
1867 std::uint64_t mediaDataSize =
size() - streamOffset;
1869 mediaDataSize -= 128;
1872 if (rewriteRequired) {
1874 switch (m_containerFormat) {
1876 progress.
updateStep(
"Writing MPEG audio frames ...");
1881 backupStream.seekg(
static_cast<streamoff
>(streamOffset));
1882 CopyHelper<0x4000> copyHelper;
1887 outputStream.seekp(
static_cast<std::streamoff
>(mediaDataSize), ios_base::cur);
1892 progress.
updateStep(
"Writing ID3v1 tag ...");
1894 m_id3v1Tag->make(
stream(), diag);
1895 }
catch (
const Failure &) {
1901 if (rewriteRequired) {
1907 m_saveFilePath.clear();
1911 outputStream.close();
1913 const auto newSize =
static_cast<std::uint64_t
>(outputStream.tellp());
1914 if (newSize <
size()) {
1917 outputStream.close();
1919 auto ec = std::error_code();
1924 diag.emplace_back(
DiagLevel::Critical,
"Unable to truncate the file: " + ec.message(), context);
1929 outputStream.flush();
The AbortableProgressFeedback class provides feedback about an ongoing operation via callbacks.
bool isAborted() const
Returns whether the operation has been aborted via tryToAbort().
void parseHeader(Diagnostics &diag, AbortableProgressFeedback &progress)
Parses the header if not parsed yet.
const std::string & documentType() const
Returns a string that describes the document type if available; otherwise returns an empty string.
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
const Locale & locale() const
Returns the locale of the track if known; otherwise returns an empty locale.
MediaType mediaType() const
Returns the media type if known; otherwise returns MediaType::Other.
The BasicFileInfo class provides basic file information such as file name, extension,...
void reportPathChanged(std::string_view newPath)
Call this function to report that the path changed.
const std::string & path() const
Returns the path of the current file.
std::uint64_t size() const
Returns size of the current file in bytes.
virtual void invalidated()
This function is called when the BasicFileInfo gets invalidated.
CppUtilities::NativeFileStream & stream()
Returns the std::fstream for the current instance.
void open(bool readOnly=false)
Opens a std::fstream for the current file.
void close()
A possibly opened std::fstream will be closed.
static std::string_view pathForOpen(std::string_view url)
Returns removes the "file:/" prefix from url to be able to pass it to functions like open(),...
void reportSizeChanged(std::uint64_t newSize)
Call this function to report that the size changed.
void updateStep(const std::string &step, std::uint8_t stepPercentage=0)
Updates the current step and invokes the first callback specified on construction.
void updateStepPercentage(std::uint8_t stepPercentage)
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...
std::size_t insertFields(const FieldMapBasedTag< ImplementationType > &from, bool overwrite)
Inserts all fields from another tag of the same field type and compare function.
Implementation of TagParser::AbstractTrack for raw FLAC streams.
VorbisComment * createVorbisComment()
Creates a new Vorbis comment for the stream.
std::uint32_t paddingSize() const
Returns the padding size.
bool removeVorbisComment()
Removes the assigned Vorbis comment if one is assigned; does nothing otherwise.
VorbisComment * vorbisComment() const
Returns the Vorbis comment if one is present in the stream.
const std::vector< std::unique_ptr< TrackType > > & tracks() const
Returns the tracks of the file.
void validateElementStructure(Diagnostics &diag, AbortableProgressFeedback &progress, std::uint64_t *paddingSize=nullptr)
Parses all elements the file consists of.
const std::vector< std::unique_ptr< TagType > > & tags() const
Returns the tags of the file.
Implementation of TagParser::Tag for ID3v1 tags.
void ensureTextValuesAreProperlyEncoded() override
Ensures the encoding of all assigned text values is supported by the tag by converting the character ...
Implementation of TagParser::Tag for ID3v2 tags.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Implementation of GenericContainer<MediaFileInfo, MatroskaTag, MatroskaTrack, EbmlElement>.
Implementation of GenericContainer<MediaFileInfo, Mp4Tag, Mp4Track, Mp4Atom>.
Implementation of TagParser::Tag for the MP4 container.
The exception that is thrown when the data to be parsed holds no parsable information (e....
This exception is thrown when the an operation is invoked that has not been implemented yet.
Implementation of TagParser::AbstractContainer for Ogg files.
void setChecksumValidationEnabled(bool enabled)
Sets whether checksum validation is enabled.
std::size_t tagCount() const override
Returns the number of tags attached to the container.
void removeAllTags() override
Actually just flags all tags as removed and clears all assigned fields.
OggVorbisComment * createTag(const TagTarget &target) override
Creates a new tag.
The exception that is thrown when an operation has been stopped and thus not successfully completed b...
The TagTarget class specifies the target of a tag.
The Tag class is used to store, read and write tag information.
std::uint64_t size() const
Returns the size the tag within the file it is parsed from in bytes.
virtual std::size_t insertValues(const Tag &from, bool overwrite)
Inserts all compatible values from another Tag.
TAG_PARSER_EXPORT void handleFailureAfterFileModifiedCanonical(MediaFileInfo &fileInfo, const std::string &originalPath, const std::string &backupPath, CppUtilities::NativeFileStream &outputStream, CppUtilities::NativeFileStream &backupStream, Diagnostics &diag, const std::string &context="making file")
Handles a failure/abort which occurred after the file has been modified.
TAG_PARSER_EXPORT void createBackupFileCanonical(const std::string &backupDir, std::string &originalPath, std::string &backupPath, CppUtilities::NativeFileStream &originalStream, CppUtilities::NativeFileStream &backupStream)
Creates a backup file like createBackupFile() but canonicalizes originalPath before doing the backup.
Contains all classes and functions of the TagInfo library.
TAG_PARSER_EXPORT std::string_view containerFormatAbbreviation(ContainerFormat containerFormat, MediaType mediaType=MediaType::Unknown, unsigned int version=0)
Returns the abbreviation of the container format as C-style string considering the specified media ty...
@ Id3TransferValuesOnRemoval
@ MergeMultipleSuccessiveId3v2Tags
@ TreatUnknownFilesAsMp3Files
@ KeepExistingId3v2Version
TAG_PARSER_EXPORT ContainerFormat parseSignature(const char *buffer, std::size_t bufferSize)
TAG_PARSER_EXPORT std::string_view containerMimeType(ContainerFormat containerFormat, MediaType mediaType=MediaType::Unknown)
Returns the MIME-type of the container format as C-style string.
ParsingStatus
The ParsingStatus enum specifies whether a certain part of the file (tracks, tags,...
MediaType
The MediaType enum specifies the type of media data (audio, video, text, ...).
MediaFileStructureFlags
The MediaFileStructureFlags enum specifies flags which describing the structure of a media file.
ContainerFormat
Specifies the container format.
MediaFileHandlingFlags
The MediaFileHandlingFlags enum specifies flags which controls the behavior of MediaFileInfo objects.
@ NormalizeKnownTagFieldIds
@ PreserveRawTimingValues
const LocaleDetail & someAbbreviatedName(LocaleFormat preferredFormat=LocaleFormat::BCP_47) const
Returns some abbreviated name, preferably of the specified preferredFormat.
The TagSettings struct contains settings which can be passed to MediaFileInfo::createAppropriateTags(...
std::vector< TagTarget > requiredTargets
Specifies the required targets. If targets are not supported by the container an informal notificatio...
std::uint8_t id3v2MajorVersion
Specifies the ID3v2 version to be used in case an ID3v2 tag present or will be created....
TagUsage id3v2usage
Specifies the usage of ID3v2 when creating tags for MP3 files (has no effect when the file is no MP3 ...
TagCreationFlags flags
Specifies options to control the tag creation. See TagSettings::Flags.
TagUsage id3v1usage
Specifies the usage of ID3v1 when creating tags for MP3 files (has no effect when the file is no MP3 ...