mirror of
https://github.com/taglib/taglib.git
synced 2026-02-08 00:10:15 -05:00
Implement complex properties for Matroska
This commit is contained in:
@ -18,8 +18,8 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef HAS_MATROSKAATTACHEDFILE_H
|
||||
#define HAS_MATROSKAATTACHEDFILE_H
|
||||
#ifndef TAGLIB_MATROSKAATTACHEDFILE_H
|
||||
#define TAGLIB_MATROSKAATTACHEDFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
|
||||
|
||||
@ -53,6 +53,11 @@ const Matroska::Attachments::AttachedFileList &Matroska::Attachments::attachedFi
|
||||
return d->files;
|
||||
}
|
||||
|
||||
Matroska::Attachments::AttachedFileList &Matroska::Attachments::attachedFiles()
|
||||
{
|
||||
return d->files;
|
||||
}
|
||||
|
||||
bool Matroska::Attachments::render()
|
||||
{
|
||||
EBML::MkAttachments attachments;
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef HAS_MATROSKAATTACHMENTS_H
|
||||
#define HAS_MATROSKAATTACHMENTS_H
|
||||
#ifndef TAGLIB_MATROSKAATTACHMENTS_H
|
||||
#define TAGLIB_MATROSKAATTACHMENTS_H
|
||||
|
||||
#include <memory>
|
||||
#include "taglib_export.h"
|
||||
@ -56,6 +56,7 @@ namespace TagLib {
|
||||
|
||||
// private Element implementation
|
||||
bool render() override;
|
||||
AttachedFileList &attachedFiles();
|
||||
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AttachmentsPrivate> d;
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef HAS_MATROSKACUES_H
|
||||
#define HAS_MATROSKACUES_H
|
||||
#ifndef TAGLIB_MATROSKACUES_H
|
||||
#define TAGLIB_MATROSKACUES_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "tlist.h"
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef HAS_MATROSKAELEMENT_H
|
||||
#define HAS_MATROSKAELEMENT_H
|
||||
#ifndef TAGLIB_MATROSKAELEMENT_H
|
||||
#define TAGLIB_MATROSKAELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#include "matroskafile.h"
|
||||
#include "matroskatag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "matroskaseekhead.h"
|
||||
#include "matroskasegment.h"
|
||||
#include "ebmlutils.h"
|
||||
@ -29,9 +30,9 @@
|
||||
#include "tlist.h"
|
||||
#include "tdebug.h"
|
||||
#include "tagutils.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@ -110,6 +111,132 @@ Matroska::Tag *Matroska::File::tag(bool create) const
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap Matroska::File::properties() const
|
||||
{
|
||||
return d->tag ? d->tag->properties() : PropertyMap();
|
||||
}
|
||||
|
||||
void Matroska::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
if(d->tag) {
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap Matroska::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(!d->tag) {
|
||||
d->tag = std::make_unique<Tag>();
|
||||
}
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
String keyForAttachedFile(const Matroska::AttachedFile *attachedFile)
|
||||
{
|
||||
if(!attachedFile) {
|
||||
return {};
|
||||
}
|
||||
if(attachedFile->mediaType().startsWith("image/")) {
|
||||
return "PICTURE";
|
||||
}
|
||||
if(!attachedFile->mediaType().isEmpty()) {
|
||||
return attachedFile->mediaType();
|
||||
}
|
||||
if(!attachedFile->fileName().isEmpty()) {
|
||||
return attachedFile->fileName();
|
||||
}
|
||||
return String::fromLongLong(attachedFile->uid());
|
||||
}
|
||||
|
||||
unsigned long long stringToULongLong(const String &str, bool *ok)
|
||||
{
|
||||
const wchar_t *beginPtr = str.toCWString();
|
||||
wchar_t *endPtr;
|
||||
errno = 0;
|
||||
const unsigned long long value = ::wcstoull(beginPtr, &endPtr, 10);
|
||||
if(ok) {
|
||||
*ok = errno == 0 && endPtr > beginPtr && *endPtr == L'\0';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StringList Matroska::File::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys = TagLib::File::complexPropertyKeys();
|
||||
if(d->attachments) {
|
||||
const auto &attachedFiles = d->attachments->attachedFileList();
|
||||
for(const auto &attachedFile : attachedFiles) {
|
||||
String key = keyForAttachedFile(attachedFile);
|
||||
if(!key.isEmpty() && !keys.contains(key)) {
|
||||
keys.append(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> Matroska::File::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props = TagLib::File::complexProperties(key);
|
||||
if(d->attachments) {
|
||||
const auto &attachedFiles = d->attachments->attachedFileList();
|
||||
for(const auto &attachedFile : attachedFiles) {
|
||||
if(keyForAttachedFile(attachedFile) == key) {
|
||||
VariantMap property;
|
||||
property.insert("data", attachedFile->data());
|
||||
property.insert("mimeType", attachedFile->mediaType());
|
||||
property.insert("description", attachedFile->description());
|
||||
property.insert("fileName", attachedFile->fileName());
|
||||
property.insert("uid", attachedFile->uid());
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
bool Matroska::File::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
if(TagLib::File::setComplexProperties(key, value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
attachments(true)->clear();
|
||||
for(const auto &property : value) {
|
||||
auto mimeType = property.value("mimeType").value<String>();
|
||||
auto data = property.value("data").value<ByteVector>();
|
||||
auto fileName = property.value("fileName").value<String>();
|
||||
auto uid = property.value("uid").value<unsigned long long>();
|
||||
bool ok;
|
||||
unsigned long long uidKey;
|
||||
if(key.upper() == "PICTURE" && !mimeType.startsWith("image/")) {
|
||||
mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")
|
||||
? "image/png" : "image/jpeg";
|
||||
}
|
||||
else if(mimeType.isEmpty() && key.find("/") != -1) {
|
||||
mimeType = key;
|
||||
}
|
||||
else if(fileName.isEmpty() && key.find(".") != -1) {
|
||||
fileName = key;
|
||||
}
|
||||
else if(!uid && ((uidKey = stringToULongLong(key, &ok))) && ok) {
|
||||
uid = uidKey;
|
||||
}
|
||||
auto attachedFile = new AttachedFile;
|
||||
attachedFile->setData(data);
|
||||
attachedFile->setMediaType(mimeType);
|
||||
attachedFile->setDescription(property.value("description").value<String>());
|
||||
attachedFile->setFileName(fileName);
|
||||
attachedFile->setUID(uid);
|
||||
d->attachments->addAttachedFile(attachedFile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Matroska::Attachments *Matroska::File::attachments(bool create) const
|
||||
{
|
||||
if(!d->attachments && create)
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef HAS_MATROSKAFILE_H
|
||||
#define HAS_MATROSKAFILE_H
|
||||
#ifndef TAGLIB_MATROSKAFILE_H
|
||||
#define TAGLIB_MATROSKAFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
@ -30,23 +30,118 @@ namespace TagLib::Matroska {
|
||||
class Properties;
|
||||
class Tag;
|
||||
class Attachments;
|
||||
|
||||
/*!
|
||||
* Implementation of TagLib::File for Matroska.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Constructs a Matroska file from \a file. If \a readProperties is \c true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* The \a readStyle parameter is currently unused.
|
||||
*/
|
||||
explicit File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle readStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska file from \a stream. If \a readProperties is \c true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* The \a readStyle parameter is currently unused.
|
||||
*/
|
||||
explicit File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle readStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
AudioProperties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the tag of the file.
|
||||
*
|
||||
* It will create a tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note The tag <b>is still</b> owned by the Matroska::File and should not
|
||||
* be deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*/
|
||||
TagLib::Tag *tag() const override;
|
||||
Attachments *attachments(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the Matroska tag of the file.
|
||||
*
|
||||
* If \a create is \c false this may return a null pointer if there is no tag.
|
||||
* If \a create is \c true it will create a tag if one does not exist and
|
||||
* returns a valid pointer.
|
||||
*
|
||||
* \note The tag <b>is still</b> owned by the Matroska::File and should not
|
||||
* be deleted by the user. It will be deleted when the file (object)
|
||||
* destroyed.
|
||||
*/
|
||||
Tag *tag(bool create) const;
|
||||
|
||||
/*!
|
||||
* Implements the reading part of the unified property interface.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the writing part of the unified tag dictionary interface.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the keys for attached files, "PICTURE" for images, the media
|
||||
* type, file name or UID for other attached files.
|
||||
* The names of the binary simple tags are included too.
|
||||
*/
|
||||
StringList complexPropertyKeys() const override;
|
||||
|
||||
/*!
|
||||
* Get the pictures stored in the attachments as complex properties
|
||||
* for \a key "PICTURE". Other attached files can be retrieved, by
|
||||
* media type, file name or UID.
|
||||
* The attached files are returned as maps with keys "data", "mimeType",
|
||||
* "description", "fileName, "uid".
|
||||
* Binary simple tags can be retrieved as maps with keys "data", "name",
|
||||
* "targetTypeValue", "language", "defaultLanguage".
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
|
||||
/*!
|
||||
* Set attached files as complex properties \a value, e.g. pictures for
|
||||
* \a key "PICTURE" with the maps in \a value having keys "data", "mimeType",
|
||||
* "description", "fileName, "uid". For other attached files, the mime type,
|
||||
* file name or UID can be used as the \a key.
|
||||
* Maps with keys "name" (with the same value as \a key) and "data" are
|
||||
* stored as binary simple tags with additional keys "targetTypeValue",
|
||||
* "language", "defaultLanguage".
|
||||
*/
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
|
||||
/*!
|
||||
* Returns the Matroska::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
AudioProperties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns \c true if the save was successful.
|
||||
*/
|
||||
bool save() override;
|
||||
//PropertyMap properties() const override { return PropertyMap(); }
|
||||
//void removeUnsupportedProperties(const StringList &properties) override { }
|
||||
|
||||
Attachments *attachments(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a Matroska
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef HAS_MATROSKASIMPLETAG_H
|
||||
#define HAS_MATROSKASIMPLETAG_H
|
||||
#ifndef TAGLIB_MATROSKASIMPLETAG_H
|
||||
#define TAGLIB_MATROSKASIMPLETAG_H
|
||||
|
||||
#include <memory>
|
||||
#include "tag.h"
|
||||
|
||||
@ -430,6 +430,73 @@ PropertyMap Matroska::Tag::setProperties(const PropertyMap &propertyMap)
|
||||
return unsupportedProperties;
|
||||
}
|
||||
|
||||
void Matroska::Tag::removeUnsupportedProperties(const StringList& properties)
|
||||
{
|
||||
d->removeSimpleTags(
|
||||
[&properties](const SimpleTag* t) {
|
||||
return properties.contains(t->name());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
StringList Matroska::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
for(const SimpleTag *t : std::as_const(d->tags)) {
|
||||
if(auto tBinary = dynamic_cast<const SimpleTagBinary*>(t)) {
|
||||
keys.append(tBinary->name());
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> Matroska::Tag::complexProperties(const String& key) const
|
||||
{
|
||||
List<VariantMap> props;
|
||||
if(key.upper() != "PICTURE") { // Pictures are handled at the file level
|
||||
for(const SimpleTag *t : std::as_const(d->tags)) {
|
||||
if(auto tBinary = dynamic_cast<const SimpleTagBinary*>(t)) {
|
||||
VariantMap property;
|
||||
property.insert("data", tBinary->value());
|
||||
property.insert("name", tBinary->name());
|
||||
property.insert("targetTypeValue", tBinary->targetTypeValue());
|
||||
property.insert("language", tBinary->language());
|
||||
property.insert("defaultLanguage", tBinary->defaultLanguageFlag());
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TagLib::Tag::complexProperties(key);
|
||||
}
|
||||
|
||||
bool Matroska::Tag::setComplexProperties(const String& key, const List<VariantMap>& value)
|
||||
{
|
||||
if(key.upper() == "PICTURE") {
|
||||
// Pictures are handled at the file level
|
||||
return false;
|
||||
}
|
||||
d->removeSimpleTags(
|
||||
[&key](const SimpleTag* t) {
|
||||
return t->name() == key && dynamic_cast<const SimpleTagBinary*>(t) != nullptr;
|
||||
}
|
||||
);
|
||||
bool result = false;
|
||||
for(const auto &property : value) {
|
||||
if(property.value("name").value<String>() == key && property.contains("data")) {
|
||||
auto *t = new SimpleTagBinary;
|
||||
t->setTargetTypeValue(static_cast<SimpleTag::TargetTypeValue>(
|
||||
property.value("targetTypeValue", 0).value<int>()));
|
||||
t->setName(key);
|
||||
t->setValue(property.value("data").value<ByteVector>());
|
||||
t->setLanguage(property.value("language").value<String>());
|
||||
t->setDefaultLanguageFlag(property.value("defaultLanguage", true).value<bool>());
|
||||
d->tags.append(t);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PropertyMap Matroska::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
|
||||
@ -63,6 +63,30 @@ namespace TagLib {
|
||||
bool isEmpty() const override;
|
||||
PropertyMap properties() const override;
|
||||
PropertyMap setProperties(const PropertyMap &propertyMap) override;
|
||||
void removeUnsupportedProperties(const StringList& properties) override;
|
||||
|
||||
/*!
|
||||
* Returns the names of the binary simple tags.
|
||||
*/
|
||||
StringList complexPropertyKeys() const override;
|
||||
|
||||
/*!
|
||||
* Get the binary simple tags as maps with keys "data", "name",
|
||||
* "targetTypeValue", "language", "defaultLanguage".
|
||||
* The attached files such as pictures with key "PICTURE" are available
|
||||
* with Matroska::File::complexProperties().
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String& key) const override;
|
||||
|
||||
/*!
|
||||
* Set the binary simple tags as maps with keys "data", "name",
|
||||
* "targetTypeValue", "language", "defaultLanguage".
|
||||
* The attached files such as pictures with key "PICTURE" can be set
|
||||
* with Matroska::File::setComplexProperties().
|
||||
*
|
||||
* Returns \c true if \c key can be stored as binary simple tags.
|
||||
*/
|
||||
bool setComplexProperties(const String& key, const List<VariantMap>& value) override;
|
||||
|
||||
void addSimpleTag(SimpleTag *tag);
|
||||
void removeSimpleTag(SimpleTag *tag);
|
||||
|
||||
Reference in New Issue
Block a user