9#include <c++utilities/conversion/stringbuilder.h>
10#include <c++utilities/io/binaryreader.h>
11#include <c++utilities/io/binarywriter.h>
12#include <c++utilities/io/copy.h>
50 using namespace VorbisCommentIds;
53 return std::string(album());
55 return std::string(artist());
57 return std::string(comment());
59 return std::string(cover());
61 return std::string(date());
63 return std::string(title());
65 return std::string(genre());
67 return std::string(trackNumber());
69 return std::string(diskNumber());
71 return std::string(partNumber());
73 return std::string(composer());
75 return std::string(encoder());
77 return std::string(encodedBy());
79 return std::string(encoderSettings());
81 return std::string(description());
83 return std::string(grouping());
85 return std::string(label());
87 return std::string(performer());
89 return std::string(language());
91 return std::string(lyricist());
93 return std::string(lyrics());
95 return std::string(albumArtist());
97 return std::string(conductor());
99 return std::string(copyright());
101 return std::string(license());
103 return std::string(director());
105 return std::string(isrc());
107 return std::string(rating());
109 return std::string(bpm());
111 return std::string(publisher());
113 return std::string(publisherWebpage());
115 return std::string(website());
117 return std::string(arranger());
119 return std::string();
125 using namespace VorbisCommentIds;
127 static const std::map<std::string_view, KnownField, CaseInsensitiveStringComparer> fieldMap({
168void VorbisComment::extendPositionInSetField(std::string_view field, std::string_view totalField,
const std::string &diagContext,
Diagnostics &diag)
170 auto totalValues = std::vector<std::int32_t>();
171 auto fieldsIter =
fields().equal_range(std::string(totalField));
172 auto fieldsDist = std::distance(fieldsIter.first, fieldsIter.second);
176 totalValues.reserve(
static_cast<std::size_t
>(fieldsDist));
177 for (; fieldsIter.first != fieldsIter.second;) {
179 totalValues.emplace_back(fieldsIter.first->second.value().toInteger());
180 fields().erase(fieldsIter.first++);
181 }
catch (
const ConversionException &e) {
182 diag.emplace_back(
DiagLevel::Warning, argsToString(
"Unable to parse \"", totalField,
"\" as integer: ", e.what()), diagContext);
183 totalValues.emplace_back(0);
188 auto totalIter = totalValues.begin(), totalEnd = totalValues.end();
189 for (fieldsIter =
fields().equal_range(std::string(field)); fieldsIter.first != fieldsIter.second && totalIter != totalEnd;
190 ++fieldsIter.first, ++totalIter) {
191 auto &v = fieldsIter.first->second.value();
193 auto p = v.toPositionInSet();
194 if (p.total() && p.total() != *totalIter) {
196 argsToString(
"The \"", totalField,
"\" field value (", *totalIter,
") does not match \"", field,
"\" field value (", p.total(),
197 "). Discarding the former in favor of the latter."),
200 p.setTotal(*totalIter);
203 }
catch (
const ConversionException &e) {
205 argsToString(
"Unable to parse \"", field,
"\" as position in set for incorporating \"", totalField,
"\": ", e.what()), diagContext);
208 if (totalIter != totalEnd) {
210 DiagLevel::Warning, argsToString(
"Vorbis Comment contains more \"", totalField,
"\" fields than \"", field,
"\" fields."), diagContext);
212 for (; totalIter != totalEnd; ++totalIter) {
213 fields().insert(std::make_pair(field, VorbisCommentField(std::string(field), TagValue(
PositionInSet(0, *totalIter)))));
221void VorbisComment::convertTotalFields(
const std::string &diagContext, Diagnostics &diag)
231template <
class StreamType>
232void VorbisComment::internalParse(StreamType &stream, std::uint64_t maxSize,
VorbisCommentFlags flags, std::uint64_t &padding, Diagnostics &diag)
235 static const string context(
"parsing Vorbis comment");
236 const auto startOffset =
static_cast<std::uint64_t
>(stream.tellg());
241 if (!skipSignature) {
244 skipSignature = (BE::toInt<std::uint64_t>(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
251 const auto vendorSize = LE::toUInt32(sig);
252 if (vendorSize <= maxSize) {
253 auto buff = make_unique<char[]>(vendorSize);
254 stream.read(buff.get(), vendorSize);
259 throw TruncatedDataException();
261 maxSize -= vendorSize;
267 for (std::uint32_t i = 0; i <
fieldCount; ++i) {
269 VorbisCommentField field;
271 field.parse(stream, maxSize, diag);
272 fields().emplace(field.id(), std::move(field));
273 }
catch (
const TruncatedDataException &) {
275 }
catch (
const Failure &) {
282 m_size =
static_cast<std::uint64_t
>(stream.tellg()) - startOffset;
288 const auto [first, end] =
fields().equal_range(yearFieldId);
289 for (
auto i = first; i != end; ++i) {
290 fields().emplace(dateFieldId, std::move(i->second));
292 fields().erase(first, end);
296 throw InvalidDataException();
298 }
catch (
const TruncatedDataException &) {
299 m_size =
static_cast<std::uint64_t
>(stream.tellg()) - startOffset;
305 if constexpr (std::is_same_v<std::decay_t<StreamType>, OggIterator>) {
306 auto bytesRemaining = std::uint64_t();
308 bytesRemaining = stream.remainingBytesInCurrentSegment();
309 if (stream.currentPage().isLastSegmentUnconcluded()) {
310 stream.nextSegment();
312 bytesRemaining += stream.remainingBytesInCurrentSegment();
316 if (bytesRemaining) {
317 diag.emplace_back(
DiagLevel::Information, argsToString(bytesRemaining,
" bytes left in last segment."), context);
318 padding += bytesRemaining;
323 convertTotalFields(context, diag);
336 auto padding = std::uint64_t();
337 internalParse(iterator, iterator.
streamSize(), flags, padding, diag);
349 internalParse(iterator, iterator.
streamSize(), flags, padding, diag);
361 auto padding = std::uint64_t();
362 internalParse(stream, maxSize, flags, padding, diag);
372 if (field.
make(writer, flags, diag)) {
375 }
catch (
const Failure &) {
391 static const string context(
"making Vorbis comment");
395 }
catch (
const ConversionException &) {
396 diag.emplace_back(
DiagLevel::Warning,
"Can not convert the assigned vendor to string.", context);
398 BinaryWriter writer(&stream);
401 static const char sig[7] = { 0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73 };
402 stream.write(sig,
sizeof(sig));
405 writer.writeUInt32LE(
static_cast<std::uint32_t
>(
vendor.size()));
406 writer.writeString(
vendor);
408 const auto fieldCountOffset = stream.tellp();
409 writer.writeUInt32LE(0);
411 std::uint32_t fieldsWritten = 0;
413 for (
auto &i :
fields()) {
414 if (i.first != coverId) {
415 fieldsWritten += makeField(i.second, writer, flags, diag);
418 if (
const auto cover =
fields().find(coverId); cover !=
fields().end()) {
419 fieldsWritten += makeField(cover->second, writer, flags, diag);
422 const auto framingByteOffset = stream.tellp();
423 stream.seekp(fieldCountOffset);
424 writer.writeUInt32LE(fieldsWritten);
425 stream.seekp(framingByteOffset);
The Diagnostics class is a container for DiagMessage.
bool setValue(const IdentifierType &id, const TagValue &value)
Assigns the given value to the field with the specified id.
typename FieldMapBasedTagTraits< VorbisComment >::FieldType::IdentifierType IdentifierType
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
KnownField knownField(const IdentifierType &id) const
std::size_t fieldCount() const
The OggIterator class helps iterating through all segments of an Ogg bitstream.
std::uint64_t streamSize() const
Returns the stream size (which has been specified when constructing the iterator).
TagValue & value()
Returns the value of the current TagField.
The TagValue class wraps values of different types.
void assignData(const char *data, std::size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
std::string toString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::string representation.
bool isEmpty() const
Returns whether no or an empty value is assigned.
#define CHECK_MAX_SIZE(sizeDenotation)
Throws TruncatedDataException() if the specified sizeDenotation exceeds maxSize; otherwise maxSize is...
Contains all classes and functions of the TagInfo library.
KnownField
Specifies the field.
VorbisCommentFlags
The VorbisCommentFlags enum specifies flags which controls parsing and making of Vorbis comments.